diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..652558d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,18 @@ +**/*.pyc +**/.*.swp +**/.coverage +**/__pycache__ +.dockerignore +.env +.envrc +.git +.gitignore +.idea +.isort.cfg +.vagrant +Dockerfile +Vagrantfile +docker-compose.yml +docs +media +static diff --git a/.gitignore b/.gitignore index e90e027..9577e06 100644 --- a/.gitignore +++ b/.gitignore @@ -50,9 +50,11 @@ coverage-report/ .idea/ .env +.envrc /docker/django_media /docker/django_static !/docker/django_media/.empty !/docker/django_static/.empty +/media/ /static/ diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 0000000..b1316d4 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,7 @@ +[tool.isort] +multi_line_output = 3 +include_trailing_comma = True +force_grid_wrap = 0 +use_parentheses = True +ensure_newline_before_comments = True +line_length = 88 diff --git a/Dockerfile b/Dockerfile index 53ba4cc..93b979c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,56 +1,70 @@ ARG DEBIAN_RELEASE=buster +FROM debian:$DEBIAN_RELEASE AS builder + +ARG GVAAPP=gva +ARG POETRY_VERSION=1.3.1 + +ENV LC_ALL=C.UTF-8 +ENV LANG=C.UTF-8 +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + curl \ + git \ + libpq-dev \ + python3-dev \ + python3-setuptools \ + python3-virtualenv \ + python3-wheel + +RUN curl -sSL https://install.python-poetry.org | POETRY_HOME=/root/.local POETRY_VERSION=$POETRY_VERSION python3 - \ + && /root/.local/bin/poetry config virtualenvs.in-project true + +WORKDIR /srv/$GVAAPP + +COPY poetry.lock pyproject.toml /srv/$GVAAPP/ + +RUN /root/.local/bin/poetry install --only=main + FROM debian:$DEBIAN_RELEASE LABEL maintainer="Jan Dittberner " ENV LC_ALL=C.UTF-8 ENV LANG=C.UTF-8 +ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update \ - && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - build-essential \ - dumb-init \ - gettext \ - git \ - python3-dev \ - python3-pip \ - python3-setuptools \ - python3-virtualenv \ - python3-wheel \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ + dumb-init \ + gettext \ + postgresql-client \ + python3 \ + python3-pip \ + python3-wheel \ && apt-get clean \ - && rm -rf /var/lib/apt/lists/*.* - -RUN python3 -m pip install --prefix=/usr/local pipenv - -RUN apt-get update \ - && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ - libpq-dev \ - postgresql-client \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/*.* + && rm -rf /var/cache/apt/archives /var/lib/apt/lists/* +ARG GVAAPP=gva ARG GVAGID=2000 ARG GVAUID=2000 -ARG GVAAPP=gva +RUN addgroup --gid $GVAGID $GVAAPP ; \ + adduser --home /home/$GVAAPP --shell /bin/bash --uid $GVAUID --gid $GVAGID --disabled-password \ + --gecos "User for gnuviechadmin component $GVAAPP" $GVAAPP -VOLUME /srv/$GVAAPP/media /srv/$GVAAPP/static +COPY --chown=$GVAAPP:$GVAAPP --from=builder /srv/$GVAAPP/.venv /srv/$GVAAPP/.venv WORKDIR /srv/$GVAAPP -COPY Pipfile Pipfile.lock /srv/$GVAAPP/ +VOLUME /srv/$GVAAPP/media /srv/$GVAAPP/static -RUN addgroup --gid $GVAGID $GVAAPP ; \ - adduser --home /home/$GVAAPP --shell /bin/bash --uid $GVAUID --gid $GVAGID --disabled-password --gecos "User for gnuviechadmin component $GVAAPP" $GVAAPP - -USER $GVAAPP -RUN python3 -m virtualenv --python=python3 /home/$GVAAPP/$GVAAPP-venv ; \ - /home/$GVAAPP/$GVAAPP-venv/bin/python3 -m pip install -U pip ; \ - VIRTUAL_ENV=/home/$GVAAPP/$GVAAPP-venv pipenv install --deploy --ignore-pipfile --dev - -VOLUME /srv/$GVAAPP +VOLUME /srv/$GVAAPP/gnuviechadmin EXPOSE 8000 -COPY gva.sh /srv/ +COPY ${GVAAPP}.sh entrypoint.sh /srv/ -ENTRYPOINT ["dumb-init", "/srv/gva.sh"] +ENTRYPOINT ["dumb-init", "/srv/entrypoint.sh"] diff --git a/Pipfile b/Pipfile deleted file mode 100644 index 0c6343e..0000000 --- a/Pipfile +++ /dev/null @@ -1,33 +0,0 @@ -[[source]] -url = "https://pypi.org/simple" -verify_ssl = true -name = "pypi" - -[[source]] -url = "https://pypi.gnuviech-server.de/simple" -verify_ssl = true -name = "gnuviech" - -[packages] -"psycopg2" = "*" -Django = "<3" -celery = "*" -django-allauth = "*" -django-braces = "*" -django-crispy-forms = "*" -django-model-utils = "*" -gvacommon = {version = "*",index = "gnuviech"} -passlib = "*" -redis = "*" -requests-oauthlib = "*" - -[dev-packages] -coverage = "*" -django-debug-toolbar = "*" -sphinx = "*" -releases = "*" -sphinxcontrib-blockdiag = "*" -pylama = "*" - -[requires] -python_version = "3.7" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 50db093..0000000 --- a/Pipfile.lock +++ /dev/null @@ -1,617 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "1c0b7bdab385f10279c852fa7fe7ae2c022dc1c4495d0e55fd407aea947bc976" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.7" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - }, - { - "name": "gnuviech", - "url": "https://pypi.gnuviech-server.de/simple", - "verify_ssl": true - } - ] - }, - "default": { - "amqp": { - "hashes": [ - "sha256:6e649ca13a7df3faacdc8bbb280aa9a6602d22fd9d545336077e573a1f4ff3b8", - "sha256:77f1aef9410698d20eaeac5b73a87817365f457a507d82edf292e12cbb83b08d" - ], - "version": "==2.5.2" - }, - "billiard": { - "hashes": [ - "sha256:bff575450859a6e0fbc2f9877d9b715b0bbc07c3565bb7ed2280526a0cdf5ede", - "sha256:d91725ce6425f33a97dfa72fb6bfef0e47d4652acd98a032bd1a7fbf06d5fa6a" - ], - "version": "==3.6.3.0" - }, - "celery": { - "hashes": [ - "sha256:108a0bf9018a871620936c33a3ee9f6336a89f8ef0a0f567a9001f4aa361415f", - "sha256:5b4b37e276033fe47575107a2775469f0b721646a08c96ec2c61531e4fe45f2a" - ], - "index": "pypi", - "version": "==4.4.2" - }, - "certifi": { - "hashes": [ - "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304", - "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519" - ], - "version": "==2020.4.5.1" - }, - "chardet": { - "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" - ], - "version": "==3.0.4" - }, - "defusedxml": { - "hashes": [ - "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93", - "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5" - ], - "version": "==0.6.0" - }, - "django": { - "hashes": [ - "sha256:69897097095f336d5aeef45b4103dceae51c00afa6d3ae198a2a18e519791b7a", - "sha256:6ecd229e1815d4fc5240fc98f1cca78c41e7a8cd3e3f2eefadc4735031077916" - ], - "index": "pypi", - "version": "==2.2.12" - }, - "django-allauth": { - "hashes": [ - "sha256:7ab91485b80d231da191d5c7999ba93170ef1bf14ab6487d886598a1ad03e1d8" - ], - "index": "pypi", - "version": "==0.41.0" - }, - "django-braces": { - "hashes": [ - "sha256:83705b78948de00804bfacf40c315d001bb39630f35bbdd8588211c2d5b4d43f", - "sha256:a6d9b34cf3e4949635e54884097c30410d7964fc7bec7231445ea7079b8c5722" - ], - "index": "pypi", - "version": "==1.14.0" - }, - "django-crispy-forms": { - "hashes": [ - "sha256:50032184708ce351e3c9f0008ac35d659d9d5973fa2db218066f2e0a76eb41d9", - "sha256:67e73ac863d3159500029fbbcdcb788f287a3fd357becebc1a0b51f73896dce3" - ], - "index": "pypi", - "version": "==1.9.0" - }, - "django-model-utils": { - "hashes": [ - "sha256:9cf882e5b604421b62dbe57ad2b18464dc9c8f963fc3f9831badccae66c1139c", - "sha256:adf09e5be15122a7f4e372cb5a6dd512bbf8d78a23a90770ad0983ee9d909061" - ], - "index": "pypi", - "version": "==4.0.0" - }, - "gvacommon": { - "hashes": [ - "sha256:adf1ebc824433196d112764c61d9ca869481d33f612818c2840069f57ab42c25" - ], - "index": "gnuviech", - "version": "==0.5.0" - }, - "idna": { - "hashes": [ - "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb", - "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa" - ], - "version": "==2.9" - }, - "importlib-metadata": { - "hashes": [ - "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f", - "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e" - ], - "markers": "python_version < '3.8'", - "version": "==1.6.0" - }, - "kombu": { - "hashes": [ - "sha256:2d1cda774126a044d91a7ff5fa6d09edf99f46924ab332a810760fe6740e9b76", - "sha256:598e7e749d6ab54f646b74b2d2df67755dee13894f73ab02a2a9feb8870c7cb2" - ], - "version": "==4.6.8" - }, - "oauthlib": { - "hashes": [ - "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889", - "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea" - ], - "version": "==3.1.0" - }, - "passlib": { - "hashes": [ - "sha256:68c35c98a7968850e17f1b6892720764cc7eed0ef2b7cb3116a89a28e43fe177", - "sha256:8d666cef936198bc2ab47ee9b0410c94adf2ba798e5a84bf220be079ae7ab6a8" - ], - "index": "pypi", - "version": "==1.7.2" - }, - "psycopg2": { - "hashes": [ - "sha256:132efc7ee46a763e68a815f4d26223d9c679953cd190f1f218187cb60decf535", - "sha256:2327bf42c1744a434ed8ed0bbaa9168cac7ee5a22a9001f6fc85c33b8a4a14b7", - "sha256:27c633f2d5db0fc27b51f1b08f410715b59fa3802987aec91aeb8f562724e95c", - "sha256:2c0afb40cfb4d53487ee2ebe128649028c9a78d2476d14a67781e45dc287f080", - "sha256:2df2bf1b87305bd95eb3ac666ee1f00a9c83d10927b8144e8e39644218f4cf81", - "sha256:440a3ea2c955e89321a138eb7582aa1d22fe286c7d65e26a2c5411af0a88ae72", - "sha256:6a471d4d2a6f14c97a882e8d3124869bc623f3df6177eefe02994ea41fd45b52", - "sha256:6b306dae53ec7f4f67a10942cf8ac85de930ea90e9903e2df4001f69b7833f7e", - "sha256:a0984ff49e176062fcdc8a5a2a670c9bb1704a2f69548bce8f8a7bad41c661bf", - "sha256:ac5b23d0199c012ad91ed1bbb971b7666da651c6371529b1be8cbe2a7bf3c3a9", - "sha256:acf56d564e443e3dea152efe972b1434058244298a94348fc518d6dd6a9fb0bb", - "sha256:d3b29d717d39d3580efd760a9a46a7418408acebbb784717c90d708c9ed5f055", - "sha256:f7d46240f7a1ae1dd95aab38bd74f7428d46531f69219954266d669da60c0818" - ], - "index": "pypi", - "version": "==2.8.5" - }, - "python3-openid": { - "hashes": [ - "sha256:0086da6b6ef3161cfe50fb1ee5cceaf2cda1700019fda03c2c5c440ca6abe4fa", - "sha256:628d365d687e12da12d02c6691170f4451db28d6d68d050007e4a40065868502" - ], - "version": "==3.1.0" - }, - "pytz": { - "hashes": [ - "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d", - "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be" - ], - "version": "==2019.3" - }, - "redis": { - "hashes": [ - "sha256:0dcfb335921b88a850d461dc255ff4708294943322bd55de6cfd68972490ca1f", - "sha256:b205cffd05ebfd0a468db74f0eedbff8df1a7bfc47521516ade4692991bb0833" - ], - "index": "pypi", - "version": "==3.4.1" - }, - "requests": { - "hashes": [ - "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee", - "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6" - ], - "version": "==2.23.0" - }, - "requests-oauthlib": { - "hashes": [ - "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d", - "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a" - ], - "index": "pypi", - "version": "==1.3.0" - }, - "six": { - "hashes": [ - "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a", - "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c" - ], - "version": "==1.14.0" - }, - "sqlparse": { - "hashes": [ - "sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e", - "sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548" - ], - "version": "==0.3.1" - }, - "urllib3": { - "hashes": [ - "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", - "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc" - ], - "version": "==1.25.8" - }, - "vine": { - "hashes": [ - "sha256:133ee6d7a9016f177ddeaf191c1f58421a1dcc6ee9a42c58b34bed40e1d2cd87", - "sha256:ea4947cc56d1fd6f2095c8d543ee25dad966f78692528e68b4fada11ba3f98af" - ], - "version": "==1.3.0" - }, - "zipp": { - "hashes": [ - "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b", - "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96" - ], - "version": "==3.1.0" - } - }, - "develop": { - "alabaster": { - "hashes": [ - "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359", - "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02" - ], - "version": "==0.7.12" - }, - "asgiref": { - "hashes": [ - "sha256:8036f90603c54e93521e5777b2b9a39ba1bad05773fcf2d208f0299d1df58ce5", - "sha256:9ca8b952a0a9afa61d30aa6d3d9b570bb3fd6bafcf7ec9e6bed43b936133db1c" - ], - "version": "==3.2.7" - }, - "babel": { - "hashes": [ - "sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38", - "sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4" - ], - "version": "==2.8.0" - }, - "blockdiag": { - "hashes": [ - "sha256:16a69dd9f3b44c9e0869999ce82aa968586698febc86ece9ca0c902dba772397", - "sha256:fa0b47cf25bfc4d546b7fc284c70c3bac875a066e744b4a6b1d9ba457e4ed077" - ], - "version": "==2.0.1" - }, - "certifi": { - "hashes": [ - "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304", - "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519" - ], - "version": "==2020.4.5.1" - }, - "chardet": { - "hashes": [ - "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", - "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" - ], - "version": "==3.0.4" - }, - "coverage": { - "hashes": [ - "sha256:03f630aba2b9b0d69871c2e8d23a69b7fe94a1e2f5f10df5049c0df99db639a0", - "sha256:046a1a742e66d065d16fb564a26c2a15867f17695e7f3d358d7b1ad8a61bca30", - "sha256:0a907199566269e1cfa304325cc3b45c72ae341fbb3253ddde19fa820ded7a8b", - "sha256:165a48268bfb5a77e2d9dbb80de7ea917332a79c7adb747bd005b3a07ff8caf0", - "sha256:1b60a95fc995649464e0cd48cecc8288bac5f4198f21d04b8229dc4097d76823", - "sha256:1f66cf263ec77af5b8fe14ef14c5e46e2eb4a795ac495ad7c03adc72ae43fafe", - "sha256:2e08c32cbede4a29e2a701822291ae2bc9b5220a971bba9d1e7615312efd3037", - "sha256:3844c3dab800ca8536f75ae89f3cf566848a3eb2af4d9f7b1103b4f4f7a5dad6", - "sha256:408ce64078398b2ee2ec08199ea3fcf382828d2f8a19c5a5ba2946fe5ddc6c31", - "sha256:443be7602c790960b9514567917af538cac7807a7c0c0727c4d2bbd4014920fd", - "sha256:4482f69e0701139d0f2c44f3c395d1d1d37abd81bfafbf9b6efbe2542679d892", - "sha256:4a8a259bf990044351baf69d3b23e575699dd60b18460c71e81dc565f5819ac1", - "sha256:513e6526e0082c59a984448f4104c9bf346c2da9961779ede1fc458e8e8a1f78", - "sha256:5f587dfd83cb669933186661a351ad6fc7166273bc3e3a1531ec5c783d997aac", - "sha256:62061e87071497951155cbccee487980524d7abea647a1b2a6eb6b9647df9006", - "sha256:641e329e7f2c01531c45c687efcec8aeca2a78a4ff26d49184dce3d53fc35014", - "sha256:65a7e00c00472cd0f59ae09d2fb8a8aaae7f4a0cf54b2b74f3138d9f9ceb9cb2", - "sha256:6ad6ca45e9e92c05295f638e78cd42bfaaf8ee07878c9ed73e93190b26c125f7", - "sha256:73aa6e86034dad9f00f4bbf5a666a889d17d79db73bc5af04abd6c20a014d9c8", - "sha256:7c9762f80a25d8d0e4ab3cb1af5d9dffbddb3ee5d21c43e3474c84bf5ff941f7", - "sha256:85596aa5d9aac1bf39fe39d9fa1051b0f00823982a1de5766e35d495b4a36ca9", - "sha256:86a0ea78fd851b313b2e712266f663e13b6bc78c2fb260b079e8b67d970474b1", - "sha256:8a620767b8209f3446197c0e29ba895d75a1e272a36af0786ec70fe7834e4307", - "sha256:922fb9ef2c67c3ab20e22948dcfd783397e4c043a5c5fa5ff5e9df5529074b0a", - "sha256:9fad78c13e71546a76c2f8789623eec8e499f8d2d799f4b4547162ce0a4df435", - "sha256:a37c6233b28e5bc340054cf6170e7090a4e85069513320275a4dc929144dccf0", - "sha256:c3fc325ce4cbf902d05a80daa47b645d07e796a80682c1c5800d6ac5045193e5", - "sha256:cda33311cb9fb9323958a69499a667bd728a39a7aa4718d7622597a44c4f1441", - "sha256:db1d4e38c9b15be1521722e946ee24f6db95b189d1447fa9ff18dd16ba89f732", - "sha256:eda55e6e9ea258f5e4add23bcf33dc53b2c319e70806e180aecbff8d90ea24de", - "sha256:f372cdbb240e09ee855735b9d85e7f50730dcfb6296b74b95a3e5dea0615c4c1" - ], - "index": "pypi", - "version": "==5.0.4" - }, - "django": { - "hashes": [ - "sha256:69897097095f336d5aeef45b4103dceae51c00afa6d3ae198a2a18e519791b7a", - "sha256:6ecd229e1815d4fc5240fc98f1cca78c41e7a8cd3e3f2eefadc4735031077916" - ], - "index": "pypi", - "version": "==2.2.12" - }, - "django-debug-toolbar": { - "hashes": [ - "sha256:eabbefe89881bbe4ca7c980ff102e3c35c8e8ad6eb725041f538988f2f39a943", - "sha256:ff94725e7aae74b133d0599b9bf89bd4eb8f5d2c964106e61d11750228c8774c" - ], - "index": "pypi", - "version": "==2.2" - }, - "docutils": { - "hashes": [ - "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af", - "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc" - ], - "version": "==0.16" - }, - "funcparserlib": { - "hashes": [ - "sha256:b7992eac1a3eb97b3d91faa342bfda0729e990bd8a43774c1592c091e563c91d" - ], - "version": "==0.3.6" - }, - "idna": { - "hashes": [ - "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb", - "sha256:a068a21ceac8a4d63dbfd964670474107f541babbd2250d61922f029858365fa" - ], - "version": "==2.9" - }, - "imagesize": { - "hashes": [ - "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1", - "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1" - ], - "version": "==1.2.0" - }, - "jinja2": { - "hashes": [ - "sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250", - "sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49" - ], - "version": "==2.11.1" - }, - "markupsafe": { - "hashes": [ - "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", - "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", - "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", - "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", - "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", - "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", - "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", - "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", - "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", - "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", - "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", - "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", - "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", - "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", - "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", - "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", - "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", - "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", - "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", - "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", - "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", - "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", - "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", - "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", - "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", - "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", - "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", - "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", - "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", - "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", - "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", - "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", - "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" - ], - "version": "==1.1.1" - }, - "mccabe": { - "hashes": [ - "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", - "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" - ], - "version": "==0.6.1" - }, - "packaging": { - "hashes": [ - "sha256:3c292b474fda1671ec57d46d739d072bfd495a4f51ad01a055121d81e952b7a3", - "sha256:82f77b9bee21c1bafbf35a84905d604d5d1223801d639cf3ed140bd651c08752" - ], - "version": "==20.3" - }, - "pillow": { - "hashes": [ - "sha256:04a10558320eba9137d6a78ca6fc8f4a5801f1b971152938851dc4629d903579", - "sha256:0f89ddc77cf421b8cd34ae852309501458942bf370831b4a9b406156b599a14e", - "sha256:251e5618125ec12ac800265d7048f5857a8f8f1979db9ea3e11382e159d17f68", - "sha256:291bad7097b06d648222b769bbfcd61e40d0abdfe10df686d20ede36eb8162b6", - "sha256:2f0b52a08d175f10c8ea36685115681a484c55d24d0933f9fd911e4111c04144", - "sha256:3713386d1e9e79cea1c5e6aaac042841d7eef838cc577a3ca153c8bedf570287", - "sha256:433bbc2469a2351bea53666d97bb1eb30f0d56461735be02ea6b27654569f80f", - "sha256:4510c6b33277970b1af83c987277f9a08ec2b02cc20ac0f9234e4026136bb137", - "sha256:50a10b048f4dd81c092adad99fa5f7ba941edaf2f9590510109ac2a15e706695", - "sha256:670e58d3643971f4afd79191abd21623761c2ebe61db1c2cb4797d817c4ba1a7", - "sha256:6c1924ed7dbc6ad0636907693bbbdd3fdae1d73072963e71f5644b864bb10b4d", - "sha256:721c04d3c77c38086f1f95d1cd8df87f2f9a505a780acf8575912b3206479da1", - "sha256:8d5799243050c2833c2662b824dfb16aa98e408d2092805edea4300a408490e7", - "sha256:90cd441a1638ae176eab4d8b6b94ab4ec24b212ed4c3fbee2a6e74672481d4f8", - "sha256:a5dc9f28c0239ec2742d4273bd85b2aa84655be2564db7ad1eb8f64b1efcdc4c", - "sha256:b2f3e8cc52ecd259b94ca880fea0d15f4ebc6da2cd3db515389bb878d800270f", - "sha256:b7453750cf911785009423789d2e4e5393aae9cbb8b3f471dab854b85a26cb89", - "sha256:b99b2607b6cd58396f363b448cbe71d3c35e28f03e442ab00806463439629c2c", - "sha256:cd47793f7bc9285a88c2b5551d3f16a2ddd005789614a34c5f4a598c2a162383", - "sha256:d6bf085f6f9ec6a1724c187083b37b58a8048f86036d42d21802ed5d1fae4853", - "sha256:da737ab273f4d60ae552f82ad83f7cbd0e173ca30ca20b160f708c92742ee212", - "sha256:eb84e7e5b07ff3725ab05977ac56d5eeb0c510795aeb48e8b691491be3c5745b" - ], - "version": "==7.1.1" - }, - "pycodestyle": { - "hashes": [ - "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56", - "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c" - ], - "version": "==2.5.0" - }, - "pydocstyle": { - "hashes": [ - "sha256:da7831660b7355307b32778c4a0dbfb137d89254ef31a2b2978f50fc0b4d7586", - "sha256:f4f5d210610c2d153fae39093d44224c17429e2ad7da12a8b419aba5c2f614b5" - ], - "version": "==5.0.2" - }, - "pyflakes": { - "hashes": [ - "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92", - "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8" - ], - "version": "==2.2.0" - }, - "pygments": { - "hashes": [ - "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44", - "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324" - ], - "version": "==2.6.1" - }, - "pylama": { - "hashes": [ - "sha256:9bae53ef9c1a431371d6a8dca406816a60d547147b60a4934721898f553b7d8f", - "sha256:fd61c11872d6256b019ef1235be37b77c922ef37ac9797df6bd489996dddeb15" - ], - "index": "pypi", - "version": "==7.7.1" - }, - "pyparsing": { - "hashes": [ - "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1", - "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b" - ], - "version": "==2.4.7" - }, - "pytz": { - "hashes": [ - "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d", - "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be" - ], - "version": "==2019.3" - }, - "releases": { - "hashes": [ - "sha256:555ae4c97a671a420281c1c782e9236be25157b449fdf20b4c4b293fe93db2f1", - "sha256:cb3435ba372a6807433800fbe473760cfa781171513f670f3c4b76983ac80f18" - ], - "index": "pypi", - "version": "==1.6.3" - }, - "requests": { - "hashes": [ - "sha256:43999036bfa82904b6af1d99e4882b560e5e2c68e5c4b0aa03b655f3d7d73fee", - "sha256:b3f43d496c6daba4493e7c431722aeb7dbc6288f52a6e04e7b6023b0247817e6" - ], - "version": "==2.23.0" - }, - "semantic-version": { - "hashes": [ - "sha256:2a4328680073e9b243667b201119772aefc5fc63ae32398d6afafff07c4f54c0", - "sha256:2d06ab7372034bcb8b54f2205370f4aa0643c133b7e6dbd129c5200b83ab394b" - ], - "version": "==2.6.0" - }, - "six": { - "hashes": [ - "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a", - "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c" - ], - "version": "==1.14.0" - }, - "snowballstemmer": { - "hashes": [ - "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0", - "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52" - ], - "version": "==2.0.0" - }, - "sphinx": { - "hashes": [ - "sha256:6a099e6faffdc3ceba99ca8c2d09982d43022245e409249375edf111caf79ed3", - "sha256:b63a0c879c4ff9a4dffcb05217fa55672ce07abdeb81e33c73303a563f8d8901" - ], - "index": "pypi", - "version": "==3.0.0" - }, - "sphinxcontrib-applehelp": { - "hashes": [ - "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a", - "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58" - ], - "version": "==1.0.2" - }, - "sphinxcontrib-blockdiag": { - "hashes": [ - "sha256:51ce7cff8d25dfd4c8a753d5aa5491e6dbf280004719c49e8001e583ecda7d91", - "sha256:91fd35b64f1f25db59d80b8a5196ed4ffadf57a81f63ee207e34d53ec36d8f97" - ], - "index": "pypi", - "version": "==2.0.0" - }, - "sphinxcontrib-devhelp": { - "hashes": [ - "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e", - "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4" - ], - "version": "==1.0.2" - }, - "sphinxcontrib-htmlhelp": { - "hashes": [ - "sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f", - "sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b" - ], - "version": "==1.0.3" - }, - "sphinxcontrib-jsmath": { - "hashes": [ - "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", - "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8" - ], - "version": "==1.0.1" - }, - "sphinxcontrib-qthelp": { - "hashes": [ - "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72", - "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6" - ], - "version": "==1.0.3" - }, - "sphinxcontrib-serializinghtml": { - "hashes": [ - "sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc", - "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a" - ], - "version": "==1.1.4" - }, - "sqlparse": { - "hashes": [ - "sha256:022fb9c87b524d1f7862b3037e541f68597a730a8843245c349fc93e1643dc4e", - "sha256:e162203737712307dfe78860cc56c8da8a852ab2ee33750e33aeadf38d12c548" - ], - "version": "==0.3.1" - }, - "urllib3": { - "hashes": [ - "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", - "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc" - ], - "version": "==1.25.8" - }, - "webcolors": { - "hashes": [ - "sha256:76f360636957d1c976db7466bc71dcb713bb95ac8911944dffc55c01cb516de6", - "sha256:b8cd5d865a25c51ff1218f0c90d0c0781fc64312a49b746b320cf50de1648f6e" - ], - "version": "==1.11.1" - } - } -} diff --git a/docker-compose.yml b/docker-compose.yml index dd174b0..4e611c1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,4 @@ +--- version: "3" services: db: @@ -36,9 +37,9 @@ services: GVA_DOMAIN_NAME: localhost GVA_SITE_NAME: localhost volumes: - - "./docker/django_media:/srv/gva/media" - - "./docker/django_static:/srv/gva/static" - - ".:/srv/gva" + - "django_media:/srv/gva/media" + - "django_static:/srv/gva/static" + - "./gnuviechadmin:/srv/gva/gnuviechadmin" web: image: gnuviech/gvaweb:buster build: @@ -51,7 +52,7 @@ services: - redis env_file: ../gvaweb/.env volumes: - - "../gvaweb:/srv/gvaweb" + - "../gvaweb/gvaweb:/srv/gvaweb/gvaweb" ldap: image: gnuviech/gvaldap:buster build: @@ -64,7 +65,7 @@ services: - redis env_file: ../gvaldap/.env volumes: - - "../gvaldap:/srv/gvaldap" + - "../gvaldap/gvaldap:/srv/gvaldap/gvaldap" file: image: gnuviech/gvafile:buster build: @@ -77,7 +78,7 @@ services: - redis env_file: ../gvafile/.env volumes: - - "../gvafile:/srv/gvafile" + - "../gvafile/gvafile:/srv/gvafile/gvafile" pgsql: image: gnuviech/gvapgsql:buster build: @@ -90,7 +91,7 @@ services: - redis env_file: ../gvapgsql/.env volumes: - - "../gvapgsql:/srv/gvapgsql" + - "../gvapgsql/gvapgsql:/srv/gvapgsql/gvapgsql" mysql: image: gnuviech/gvamysql:buster build: @@ -103,7 +104,7 @@ services: - redis env_file: ../gvamysql/.env volumes: - - "../gvamysql:/srv/gvamysql" + - "../gvamysql/gvamysql:/srv/gvamysql/gvamysql" volumes: django_media: django_static: diff --git a/docs/changelog.rst b/docs/changelog.rst index a8ef494..c84da72 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,16 @@ Changelog ========= +* :release:`0.13.0 <2023-05-08>` +* :feature:`-` add REST API to retrieve and set user information as admin +* :feature:`-` add support model for offline account reset codes in new help + app +* :support:`-` remove unused PowerDNS support tables from domains app +* :feature:`-` add impersonation support for superusers +* :support:`-` remove django-braces dependency +* :support:`-` remove Twitter support +* :support:`-` update dependencies + * :release:`0.12.1 <2020-04-13>` * :bug:`7` fix handling of undefined mail domains in customer hosting package detail template diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 0000000..b8e2187 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/sh + +set -e + +chown -Rc gva.gva /srv/gva/media /srv/gva/static + +su -c /srv/gva.sh gva diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..db31233 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,46 @@ +{ + "name": "frontend", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "bootstrap": "^5.2.3", + "bootstrap-icons": "^1.10.4" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.7", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz", + "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/bootstrap": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz", + "integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.6" + } + }, + "node_modules/bootstrap-icons": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.10.4.tgz", + "integrity": "sha512-eI3HyIUmpGKRiRv15FCZccV+2sreGE2NnmH8mtxV/nPOzQVu0sPEj8HhF1MwjJ31IhjF0rgMvtYOX5VqIzcb/A==" + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..15bb51c --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "bootstrap": "^5.2.3", + "bootstrap-icons": "^1.10.4" + } +} diff --git a/gnuviechadmin/contact_form/forms.py b/gnuviechadmin/contact_form/forms.py index 4427eb6..8961c90 100644 --- a/gnuviechadmin/contact_form/forms.py +++ b/gnuviechadmin/contact_form/forms.py @@ -4,19 +4,16 @@ This module contains the form class for the contact_form app. """ from __future__ import absolute_import, unicode_literals -from django import forms -from django.conf import settings -from django.core.mail import send_mail -from django.template import RequestContext -from django.template import loader -from django.urls import reverse -from django.utils.translation import ugettext_lazy as _ - -from django.contrib.sites.models import Site -from django.contrib.sites.requests import RequestSite - from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit +from django import forms +from django.conf import settings +from django.contrib.sites.models import Site +from django.contrib.sites.requests import RequestSite +from django.core.mail import send_mail +from django.template import RequestContext, loader +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ class ContactForm(forms.Form): @@ -24,45 +21,42 @@ class ContactForm(forms.Form): This is the contact form class. """ - name = forms.CharField(max_length=100, label=_('Your name')) - email = forms.EmailField(max_length=200, label=_('Your email address')) - body = forms.CharField(widget=forms.Textarea, label=_('Your message')) + + name = forms.CharField(max_length=100, label=_("Your name")) + email = forms.EmailField(max_length=200, label=_("Your email address")) + body = forms.CharField(widget=forms.Textarea, label=_("Your message")) subject_template_name = "contact_form/contact_form_subject.txt" - template_name = 'contact_form/contact_form.txt' + template_name = "contact_form/contact_form.txt" from_email = settings.DEFAULT_FROM_EMAIL recipient_list = [mail_tuple[1] for mail_tuple in settings.MANAGERS] def __init__(self, **kwargs): - self.request = kwargs.pop('request') + self.request = kwargs.pop("request") super(ContactForm, self).__init__(**kwargs) self.helper = FormHelper() - self.helper.form_action = reverse('contact_form') - self.helper.add_input(Submit('submit', _('Send message'))) + self.helper.form_action = reverse("contact_form") + self.helper.add_input(Submit("submit", _("Send message"))) def get_context(self): if not self.is_valid(): - raise ValueError( - 'Cannot generate context from invalid contact form') + raise ValueError("Cannot generate context from invalid contact form") if Site._meta.installed: site = Site.objects.get_current() else: site = RequestSite(self.request) - return RequestContext( - self.request, dict(self.cleaned_data, site=site)) + return RequestContext(self.request, dict(self.cleaned_data, site=site)) def message(self): context = self.get_context() template_context = context.flatten() - template_context.update({ - 'remote_ip': context.request.META['REMOTE_ADDR'] - }) + template_context.update({"remote_ip": context.request.META["REMOTE_ADDR"]}) return loader.render_to_string(self.template_name, template_context) def subject(self): context = self.get_context().flatten() subject = loader.render_to_string(self.subject_template_name, context) - return ''.join(subject.splitlines()) + return "".join(subject.splitlines()) def save(self, fail_silently=False): """ @@ -74,5 +68,5 @@ class ContactForm(forms.Form): from_email=self.from_email, recipient_list=self.recipient_list, subject=self.subject(), - message=self.message() + message=self.message(), ) diff --git a/gnuviechadmin/contact_form/locale/de/LC_MESSAGES/django.po b/gnuviechadmin/contact_form/locale/de/LC_MESSAGES/django.po index ffeeaa3..6666a9c 100644 --- a/gnuviechadmin/contact_form/locale/de/LC_MESSAGES/django.po +++ b/gnuviechadmin/contact_form/locale/de/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: contact_form\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-01-29 11:04+0100\n" -"PO-Revision-Date: 2015-02-01 19:03+0100\n" +"POT-Creation-Date: 2023-04-22 13:01+0200\n" +"PO-Revision-Date: 2023-04-22 13:01+0200\n" "Last-Translator: Jan Dittberner \n" "Language-Team: Jan Dittberner \n" "Language: de\n" @@ -16,21 +16,32 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.6.10\n" +"X-Generator: Poedit 3.2.2\n" "X-Poedit-SourceCharset: UTF-8\n" -#: contact_form/forms.py:27 +#: contact_form/forms.py:25 msgid "Your name" msgstr "Ihr Name" -#: contact_form/forms.py:28 +#: contact_form/forms.py:26 msgid "Your email address" -msgstr "Ihre E-Mailadresse" +msgstr "Ihre E-Mail-Adresse" -#: contact_form/forms.py:29 +#: contact_form/forms.py:27 msgid "Your message" msgstr "Ihre Nachricht" -#: contact_form/forms.py:41 +#: contact_form/forms.py:39 msgid "Send message" msgstr "Nachricht senden" + +#: contact_form/templates/contact_form/contact_form.html:4 +#: contact_form/templates/contact_form/contact_form.html:5 +#: contact_form/templates/contact_form/contact_success.html:4 +#: contact_form/templates/contact_form/contact_success.html:5 +msgid "Contact" +msgstr "Kontakt" + +#: contact_form/templates/contact_form/contact_success.html:8 +msgid "Your message has been sent successfully." +msgstr "Ihre Nachricht wurde erfolgreich übermittelt." diff --git a/gnuviechadmin/templates/contact_form/base.html b/gnuviechadmin/contact_form/templates/contact_form/base.html similarity index 100% rename from gnuviechadmin/templates/contact_form/base.html rename to gnuviechadmin/contact_form/templates/contact_form/base.html diff --git a/gnuviechadmin/contact_form/templates/contact_form/contact_form.html b/gnuviechadmin/contact_form/templates/contact_form/contact_form.html new file mode 100644 index 0000000..533c5bb --- /dev/null +++ b/gnuviechadmin/contact_form/templates/contact_form/contact_form.html @@ -0,0 +1,23 @@ +{% extends "contact_form/base.html" %} +{% load i18n crispy_forms_tags %} + +{% block title %}{{ block.super }} - {% translate "Contact" %}{% endblock title %} +{% block page_title %}{% translate "Contact" %}{% endblock page_title %} + +{% block content %} + {% crispy form %} +{% endblock %} + +{% block extra_js %} + +{% endblock extra_js %} diff --git a/gnuviechadmin/templates/contact_form/contact_form.txt b/gnuviechadmin/contact_form/templates/contact_form/contact_form.txt similarity index 100% rename from gnuviechadmin/templates/contact_form/contact_form.txt rename to gnuviechadmin/contact_form/templates/contact_form/contact_form.txt diff --git a/gnuviechadmin/templates/contact_form/contact_form_subject.txt b/gnuviechadmin/contact_form/templates/contact_form/contact_form_subject.txt similarity index 100% rename from gnuviechadmin/templates/contact_form/contact_form_subject.txt rename to gnuviechadmin/contact_form/templates/contact_form/contact_form_subject.txt diff --git a/gnuviechadmin/contact_form/templates/contact_form/contact_success.html b/gnuviechadmin/contact_form/templates/contact_form/contact_success.html new file mode 100644 index 0000000..e251f8c --- /dev/null +++ b/gnuviechadmin/contact_form/templates/contact_form/contact_success.html @@ -0,0 +1,9 @@ +{% extends "contact_form/base.html" %} +{% load i18n %} + +{% block title %}{{ block.super }} - {% translate "Contact" %}{% endblock title %} +{% block page_title %}{% translate "Contact" %}{% endblock page_title %} + +{% block content %} +

{% translate "Your message has been sent successfully." %}

+{% endblock %} diff --git a/gnuviechadmin/contact_form/urls.py b/gnuviechadmin/contact_form/urls.py index e3f01ca..a77b1aa 100644 --- a/gnuviechadmin/contact_form/urls.py +++ b/gnuviechadmin/contact_form/urls.py @@ -2,17 +2,13 @@ URL patterns for the contact_form views. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import -from django.conf.urls import url - -from .views import ( - ContactFormView, - ContactSuccessView, -) +from django.urls import re_path +from .views import ContactFormView, ContactSuccessView urlpatterns = [ - url(r'^$', ContactFormView.as_view(), name='contact_form'), - url(r'^success/$', ContactSuccessView.as_view(), name='contact_success'), + re_path(r"^$", ContactFormView.as_view(), name="contact_form"), + re_path(r"^success/$", ContactSuccessView.as_view(), name="contact_success"), ] diff --git a/gnuviechadmin/contact_form/views.py b/gnuviechadmin/contact_form/views.py index a1f3aba..a0a4e05 100644 --- a/gnuviechadmin/contact_form/views.py +++ b/gnuviechadmin/contact_form/views.py @@ -2,14 +2,11 @@ This module defines the views of the contact_form app. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import from django.shortcuts import redirect from django.urls import reverse_lazy -from django.views.generic import ( - FormView, - TemplateView, -) +from django.views.generic import FormView, TemplateView from .forms import ContactForm @@ -19,22 +16,22 @@ class ContactFormView(FormView): This is the contact form view. """ + form_class = ContactForm - template_name = 'contact_form/contact_form.html' - success_url = reverse_lazy('contact_success') + template_name = "contact_form/contact_form.html" + success_url = reverse_lazy("contact_success") def get_form_kwargs(self, **kwargs): kwargs = super(ContactFormView, self).get_form_kwargs(**kwargs) - kwargs['request'] = self.request + kwargs["request"] = self.request return kwargs def get_initial(self): initial = super(ContactFormView, self).get_initial() currentuser = self.request.user if currentuser.is_authenticated: - initial['name'] = ( - currentuser.get_full_name() or currentuser.username) - initial['email'] = currentuser.email + initial["name"] = currentuser.get_full_name() or currentuser.username + initial["email"] = currentuser.email return initial def form_valid(self, form): @@ -47,4 +44,5 @@ class ContactSuccessView(TemplateView): This view is shown after successful contact form sending. """ - template_name = 'contact_form/contact_success.html' + + template_name = "contact_form/contact_success.html" diff --git a/gnuviechadmin/dashboard/locale/de/LC_MESSAGES/django.po b/gnuviechadmin/dashboard/locale/de/LC_MESSAGES/django.po index 71d4e75..b3e750a 100644 --- a/gnuviechadmin/dashboard/locale/de/LC_MESSAGES/django.po +++ b/gnuviechadmin/dashboard/locale/de/LC_MESSAGES/django.po @@ -7,18 +7,92 @@ msgid "" msgstr "" "Project-Id-Version: gnuviechadmin dashboard\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2015-01-17 15:59+0100\n" -"PO-Revision-Date: 2015-01-17 16:01+0100\n" +"POT-Creation-Date: 2023-04-16 22:07+0200\n" +"PO-Revision-Date: 2023-04-16 18:31+0200\n" "Last-Translator: Jan Dittberner \n" -"Language-Team: Jan Dittberner \n" +"Language-Team: Jan Dittberner \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.6.10\n" +"X-Generator: Poedit 3.2.2\n" "X-Poedit-SourceCharset: UTF-8\n" -#: dashboard/views.py:43 -msgid "You are not allowed to view this page." -msgstr "Sie haben nicht die nötigen Berechtigungen um diese Seite zu sehen." +#: dashboard/templates/dashboard/index.html:3 +msgid "Welcome" +msgstr "Willkommen" + +#: dashboard/templates/dashboard/index.html:4 +msgid "Welcome to our customer self service" +msgstr "Willkommen in unserem Selbstservice-System" + +#: dashboard/templates/dashboard/index.html:7 +#, python-format +msgid "" +"Hello %(full_name)s,
You can visit your Dashboard to view and modify your hosting " +"options." +msgstr "" +"Hallo %(full_name)s,
Sie können Ihre Startseite 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" diff --git a/gnuviechadmin/dashboard/templates/dashboard/user_dashboard.html b/gnuviechadmin/dashboard/templates/dashboard/user_dashboard.html new file mode 100644 index 0000000..538003f --- /dev/null +++ b/gnuviechadmin/dashboard/templates/dashboard/user_dashboard.html @@ -0,0 +1,47 @@ +{% extends "base.html" %} +{% load i18n %} +{% block title %}{{ block.super }} - {% blocktranslate with full_name=request.user.get_full_name trimmed %} + Dashboard for {{ full_name }} +{% endblocktranslate %}{% endblock title %} +{% block page_title %}{% blocktranslate with full_name=request.user.get_full_name trimmed %} + Dashboard for {{ full_name }} +{% endblocktranslate %}{% endblock page_title %} +{% block content %} +

{% translate "Hosting packages" %}

+
+
+ {% if hosting_packages %} + + + + + + + + + + {% for package in hosting_packages %} + + + + + + {% endfor %} + +
{% translate "Name" %}{% translate "Setup date" %}{% translate "Actions" %}
{{ package.name }} + {{ package.created }}
+ {% else %} +

+ {% if user == object %}{% translate "You have no hosting packages yet." %}{% else %} + {% translate "This user has no hosting packages assigned yet." %}{% endif %}

+ {% endif %} + {% if user.is_staff %} + {% translate "Add hosting package" %} + {% endif %} +
+
+{% endblock content %} \ No newline at end of file diff --git a/gnuviechadmin/dashboard/tests/test_views.py b/gnuviechadmin/dashboard/tests/test_views.py index 2ff835b..8827a80 100644 --- a/gnuviechadmin/dashboard/tests/test_views.py +++ b/gnuviechadmin/dashboard/tests/test_views.py @@ -2,7 +2,7 @@ Tests for :py:mod:`dashboard.views`. """ - +from django import http from django.contrib.auth import get_user_model from django.test import TestCase from django.urls import reverse @@ -13,55 +13,35 @@ TEST_USER = "test" TEST_PASSWORD = "secret" -class IndexViewTest(TestCase): - def test_index_view(self): - response = self.client.get(reverse("dashboard")) - self.assertEqual(response.status_code, 200) - self.assertTemplateUsed(response, "dashboard/index.html") - - class UserDashboardViewTest(TestCase): def _create_test_user(self): self.user = User.objects.create(username=TEST_USER) self.user.set_password(TEST_PASSWORD) self.user.save() - def test_user_dashboard_view_no_user(self): - response = self.client.get( - reverse("customer_dashboard", kwargs={"slug": TEST_USER}) - ) - self.assertEqual(response.status_code, 404) - def test_user_dashboard_view_anonymous(self): User.objects.create(username=TEST_USER) - response = self.client.get( - reverse("customer_dashboard", kwargs={"slug": TEST_USER}) - ) - self.assertEqual(response.status_code, 403) + response = self.client.get(reverse("customer_dashboard")) + self.assertEqual(response.status_code, 302) + self.assertRedirects(response, "/accounts/login/?next=/") def test_user_dashboard_view_logged_in_ok(self): self._create_test_user() self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD)) - response = self.client.get( - reverse("customer_dashboard", kwargs={"slug": TEST_USER}) - ) + response = self.client.get(reverse("customer_dashboard")) self.assertEqual(response.status_code, 200) def test_user_dashboard_view_logged_in_template(self): self._create_test_user() self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD)) response = self.client.get( - reverse("customer_dashboard", kwargs={"slug": TEST_USER}) + reverse("customer_dashboard") ) self.assertTemplateUsed(response, "dashboard/user_dashboard.html") def test_user_dashboard_view_logged_in_context_fresh(self): self._create_test_user() self.assertTrue(self.client.login(username=TEST_USER, password=TEST_PASSWORD)) - response = self.client.get( - reverse("customer_dashboard", kwargs={"slug": TEST_USER}) - ) - self.assertIn("dashboard_user", response.context) - self.assertEqual(self.user, response.context["dashboard_user"]) + response = self.client.get(reverse("customer_dashboard")) self.assertIn("hosting_packages", response.context) self.assertEqual(len(response.context["hosting_packages"]), 0) diff --git a/gnuviechadmin/dashboard/urls.py b/gnuviechadmin/dashboard/urls.py index ffa741b..6ba8ff4 100644 --- a/gnuviechadmin/dashboard/urls.py +++ b/gnuviechadmin/dashboard/urls.py @@ -1,15 +1,9 @@ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import -from django.conf.urls import url - -from .views import ( - IndexView, - UserDashboardView, -) +from django.urls import path +from .views import UserDashboardView urlpatterns = [ - url(r'^$', IndexView.as_view(), name='dashboard'), - url(r'^user/(?P[\w0-9@.+-_]+)/$', - UserDashboardView.as_view(), name='customer_dashboard'), + path("", UserDashboardView.as_view(), name="customer_dashboard"), ] diff --git a/gnuviechadmin/dashboard/views.py b/gnuviechadmin/dashboard/views.py index 1542e45..d92eb7e 100644 --- a/gnuviechadmin/dashboard/views.py +++ b/gnuviechadmin/dashboard/views.py @@ -2,47 +2,26 @@ This module defines the views for the gnuviechadmin customer dashboard. """ -from __future__ import unicode_literals - -from django.views.generic import ( - DetailView, - TemplateView, -) from django.contrib.auth import get_user_model - +from django.contrib.auth.mixins import LoginRequiredMixin +from django.shortcuts import redirect +from django.views.generic import DetailView, TemplateView from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin from hostingpackages.models import CustomerHostingPackage -class IndexView(TemplateView): - """ - This is the dashboard view. - - """ - template_name = 'dashboard/index.html' - - -class UserDashboardView(StaffOrSelfLoginRequiredMixin, DetailView): +class UserDashboardView(LoginRequiredMixin, TemplateView): """ This is the user dashboard view. """ - model = get_user_model() - context_object_name = 'dashboard_user' - slug_field = 'username' - template_name = 'dashboard/user_dashboard.html' + + template_name = "dashboard/user_dashboard.html" def get_context_data(self, **kwargs): context = super(UserDashboardView, self).get_context_data(**kwargs) - context['hosting_packages'] = CustomerHostingPackage.objects.filter( - customer=self.object + context["hosting_packages"] = CustomerHostingPackage.objects.filter( + customer=self.request.user ) return context - - def get_customer_object(self): - """ - Returns the customer object. - - """ - return self.get_object() diff --git a/gnuviechadmin/domains/__init__.py b/gnuviechadmin/domains/__init__.py index 77d3acd..f6b1dc5 100644 --- a/gnuviechadmin/domains/__init__.py +++ b/gnuviechadmin/domains/__init__.py @@ -2,4 +2,3 @@ This app takes care of domains. """ -default_app_config = 'domains.apps.DomainAppConfig' diff --git a/gnuviechadmin/domains/admin.py b/gnuviechadmin/domains/admin.py index 87be497..0369116 100644 --- a/gnuviechadmin/domains/admin.py +++ b/gnuviechadmin/domains/admin.py @@ -5,24 +5,7 @@ with the django admin site. """ from django.contrib import admin -from .models import ( - DNSComment, - DNSCryptoKey, - DNSDomain, - DNSDomainMetadata, - DNSRecord, - DNSSupermaster, - DNSTSIGKey, - HostingDomain, - MailDomain, -) +from domains.models import HostingDomain, MailDomain admin.site.register(MailDomain) admin.site.register(HostingDomain) -admin.site.register(DNSComment) -admin.site.register(DNSCryptoKey) -admin.site.register(DNSDomain) -admin.site.register(DNSDomainMetadata) -admin.site.register(DNSRecord) -admin.site.register(DNSSupermaster) -admin.site.register(DNSTSIGKey) diff --git a/gnuviechadmin/domains/apps.py b/gnuviechadmin/domains/apps.py index 12311cc..9e903e6 100644 --- a/gnuviechadmin/domains/apps.py +++ b/gnuviechadmin/domains/apps.py @@ -3,9 +3,8 @@ This module contains the :py:class:`django.apps.AppConfig` instance for the :py:mod:`domains` app. """ -from __future__ import unicode_literals from django.apps import AppConfig -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ class DomainAppConfig(AppConfig): @@ -13,5 +12,6 @@ class DomainAppConfig(AppConfig): AppConfig for the :py:mod:`domains` app. """ - name = 'domains' - verbose_name = _('Domains') + + name = "domains" + verbose_name = _("Domains") diff --git a/gnuviechadmin/domains/forms.py b/gnuviechadmin/domains/forms.py index 451e108..bd5565e 100644 --- a/gnuviechadmin/domains/forms.py +++ b/gnuviechadmin/domains/forms.py @@ -2,19 +2,15 @@ This module defines form classes for domain editing. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import import re +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Layout, Submit from django import forms from django.urls import reverse -from django.utils.translation import ugettext as _ - -from crispy_forms.helper import FormHelper -from crispy_forms.layout import ( - Layout, - Submit, -) +from django.utils.translation import gettext as _ from .models import HostingDomain @@ -26,11 +22,10 @@ def relative_domain_validator(value): """ if len(value) > 254: - raise forms.ValidationError( - _('host name too long'), code='too-long') + raise forms.ValidationError(_("host name too long"), code="too-long") allowed = re.compile(r"(?!-)[a-z\d-]{1,63}(?\n" "Language-Team: Jan Dittberner \n" "Language: de\n" @@ -16,152 +16,60 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.8.6\n" +"X-Generator: Poedit 3.2.2\n" "X-Poedit-SourceCharset: UTF-8\n" #: domains/apps.py:17 msgid "Domains" msgstr "Domains" -#: domains/forms.py:30 domains/tests/test_forms.py:24 +#: domains/forms.py:25 domains/tests/test_forms.py:20 msgid "host name too long" msgstr "zu langer Hostname" -#: domains/forms.py:33 domains/tests/test_forms.py:29 -#: domains/tests/test_forms.py:34 domains/tests/test_forms.py:39 -#: domains/tests/test_forms.py:44 +#: domains/forms.py:28 domains/tests/test_forms.py:24 +#: domains/tests/test_forms.py:28 domains/tests/test_forms.py:32 +#: domains/tests/test_forms.py:36 msgid "invalid domain name" msgstr "ungültiger Domainname" -#: domains/forms.py:56 +#: domains/forms.py:51 msgid "Add Hosting Domain" msgstr "Hostingdomain hinzufügen" -#: domains/models.py:17 -msgid "Master" -msgstr "Master" - -#: domains/models.py:18 -msgid "Slave" -msgstr "Slave" - #: domains/models.py:19 -msgid "Native" -msgstr "Native" - -#: domains/models.py:44 -msgid "HMAC MD5" -msgstr "HMAC MD5" - -#: domains/models.py:45 -msgid "HMAC SHA1" -msgstr "HMAC SHA1" - -#: domains/models.py:46 -msgid "HMAC SHA224" -msgstr "HMAC SHA224" - -#: domains/models.py:47 -msgid "HMAC SHA256" -msgstr "HMAC SHA256" - -#: domains/models.py:48 -msgid "HMAC SHA384" -msgstr "HMAC SHA384" - -#: domains/models.py:49 -msgid "HMAC SHA512" -msgstr "HMAC SHA512" - -#: domains/models.py:58 msgid "domain name" msgstr "Domainname" -#: domains/models.py:60 domains/models.py:258 domains/models.py:308 +#: domains/models.py:22 msgid "customer" msgstr "Kunde" -#: domains/models.py:76 +#: domains/models.py:41 msgid "Mail domain" msgstr "E-Maildomain" -#: domains/models.py:77 +#: domains/models.py:42 msgid "Mail domains" msgstr "E-Maildomains" -#: domains/models.py:121 +#: domains/models.py:91 msgid "mail domain" msgstr "E-Maildomain" -#: domains/models.py:122 +#: domains/models.py:94 msgid "assigned mail domain for this domain" msgstr "zugeordnete E-Maildomain für diese Domain" -#: domains/models.py:128 +#: domains/models.py:101 msgid "Hosting domain" msgstr "Hostingdomain" -#: domains/models.py:129 +#: domains/models.py:102 msgid "Hosting domains" msgstr "Hostingdomains" -#: domains/models.py:169 -msgid "DNS domain" -msgstr "DNS-Domain" - -#: domains/models.py:170 -msgid "DNS domains" -msgstr "DNS-Domains" - -#: domains/models.py:226 -msgid "DNS record" -msgstr "DNS-Record" - -#: domains/models.py:227 -msgid "DNS records" -msgstr "DNS-Records" - -#: domains/models.py:261 -msgid "DNS supermaster" -msgstr "DNS-Supermaster" - -#: domains/models.py:262 -msgid "DNS supermasters" -msgstr "DNS-Supermasters" - -#: domains/models.py:313 -msgid "DNS comment" -msgstr "DNS-Kommentar" - -#: domains/models.py:314 -msgid "DNS comments" -msgstr "DNS-Kommentare" - -#: domains/models.py:351 -msgid "DNS domain metadata item" -msgstr "DNS-Domainmetadaten-Eintrag" - -#: domains/models.py:352 -msgid "DNS domain metadata items" -msgstr "DNS-Domainmetadaten-Einträge" - -#: domains/models.py:385 -msgid "DNS crypto key" -msgstr "DNS-Cryposchlüssel" - -#: domains/models.py:386 -msgid "DNS crypto keys" -msgstr "DNS-Cryptoschlüssel" - -#: domains/models.py:420 -msgid "DNS TSIG key" -msgstr "DNS-TSIG-Schlüssel" - -#: domains/models.py:421 -msgid "DNS TSIG keys" -msgstr "DNS-TSIG-Schlüssel" - -#: domains/views.py:58 +#: domains/views.py:51 #, python-brace-format msgid "Successfully created domain {domainname}" msgstr "Domain {domainname} erfolgreich angelegt" diff --git a/gnuviechadmin/domains/migrations/0001_initial.py b/gnuviechadmin/domains/migrations/0001_initial.py index a38fce2..e7697fb 100644 --- a/gnuviechadmin/domains/migrations/0001_initial.py +++ b/gnuviechadmin/domains/migrations/0001_initial.py @@ -1,28 +1,46 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations import django.utils.timezone import model_utils.fields +from django.db import migrations, models class Migration(migrations.Migration): - - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='MailDomain', + name="MailDomain", fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, verbose_name='created', editable=False)), - ('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, verbose_name='modified', editable=False)), - ('domain', models.CharField(unique=True, max_length=128)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + verbose_name="created", + editable=False, + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + verbose_name="modified", + editable=False, + ), + ), + ("domain", models.CharField(unique=True, max_length=128)), ], options={ - 'verbose_name': 'Mail domain', - 'verbose_name_plural': 'Mail domains', + "verbose_name": "Mail domain", + "verbose_name_plural": "Mail domains", }, bases=(models.Model,), ), diff --git a/gnuviechadmin/domains/migrations/0002_auto_20150124_1909.py b/gnuviechadmin/domains/migrations/0002_auto_20150124_1909.py index 27743bf..9fd2757 100644 --- a/gnuviechadmin/domains/migrations/0002_auto_20150124_1909.py +++ b/gnuviechadmin/domains/migrations/0002_auto_20150124_1909.py @@ -1,68 +1,97 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations import django.utils.timezone -from django.conf import settings import model_utils.fields +from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('domains', '0001_initial'), + ("domains", "0001_initial"), ] operations = [ migrations.CreateModel( - name='HostingDomain', + name="HostingDomain", fields=[ - ('id', - models.AutoField(verbose_name='ID', serialize=False, - auto_created=True, primary_key=True)), - ('created', - model_utils.fields.AutoCreatedField( - default=django.utils.timezone.now, verbose_name='created', - editable=False)), - ('modified', - model_utils.fields.AutoLastModifiedField( - default=django.utils.timezone.now, verbose_name='modified', - editable=False)), - ('domain', - models.CharField( - unique=True, max_length=128, verbose_name='domain name')), - ('customer', - models.ForeignKey( - verbose_name='customer', blank=True, - to=settings.AUTH_USER_MODEL, null=True, - on_delete=models.CASCADE)), - ('maildomain', - models.OneToOneField( - null=True, to='domains.MailDomain', blank=True, - help_text='assigned mail domain for this domain', - verbose_name='mail domain', - on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + verbose_name="created", + editable=False, + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + verbose_name="modified", + editable=False, + ), + ), + ( + "domain", + models.CharField( + unique=True, max_length=128, verbose_name="domain name" + ), + ), + ( + "customer", + models.ForeignKey( + verbose_name="customer", + blank=True, + to=settings.AUTH_USER_MODEL, + null=True, + on_delete=models.CASCADE, + ), + ), + ( + "maildomain", + models.OneToOneField( + null=True, + to="domains.MailDomain", + blank=True, + help_text="assigned mail domain for this domain", + verbose_name="mail domain", + on_delete=models.CASCADE, + ), + ), ], options={ - 'verbose_name': 'Hosting domain', - 'verbose_name_plural': 'Hosting domains', + "verbose_name": "Hosting domain", + "verbose_name_plural": "Hosting domains", }, bases=(models.Model,), ), migrations.AddField( - model_name='maildomain', - name='customer', + model_name="maildomain", + name="customer", field=models.ForeignKey( - verbose_name='customer', blank=True, - to=settings.AUTH_USER_MODEL, null=True, - on_delete=models.CASCADE), + verbose_name="customer", + blank=True, + to=settings.AUTH_USER_MODEL, + null=True, + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AlterField( - model_name='maildomain', - name='domain', + model_name="maildomain", + name="domain", field=models.CharField( - unique=True, max_length=128, verbose_name='domain name'), + unique=True, max_length=128, verbose_name="domain name" + ), preserve_default=True, ), ] diff --git a/gnuviechadmin/domains/migrations/0003_auto_20151105_2133.py b/gnuviechadmin/domains/migrations/0003_auto_20151105_2133.py index 5891a27..7fd1b40 100644 --- a/gnuviechadmin/domains/migrations/0003_auto_20151105_2133.py +++ b/gnuviechadmin/domains/migrations/0003_auto_20151105_2133.py @@ -1,199 +1,285 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import migrations, models import django.utils.timezone -from django.conf import settings import model_utils.fields +from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('domains', '0002_auto_20150124_1909'), + ("domains", "0002_auto_20150124_1909"), ] operations = [ migrations.CreateModel( - name='DNSComment', + name="DNSComment", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, - auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=255)), - ('commenttype', - models.CharField(max_length=10, db_column='type')), - ('modified_at', models.IntegerField()), - ('comment', models.CharField(max_length=65535)), - ('customer', models.ForeignKey( - verbose_name='customer', - to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=255)), + ("commenttype", models.CharField(max_length=10, db_column="type")), + ("modified_at", models.IntegerField()), + ("comment", models.CharField(max_length=65535)), + ( + "customer", + models.ForeignKey( + verbose_name="customer", + to=settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + ), + ), ], ), migrations.RunSQL( - '''ALTER TABLE domains_dnscomment ADD CONSTRAINT c_lowercase_name - CHECK (((name)::TEXT = LOWER((name)::TEXT)))''' + """ALTER TABLE domains_dnscomment ADD CONSTRAINT c_lowercase_name + CHECK (((name)::TEXT = LOWER((name)::TEXT)))""" ), migrations.CreateModel( - name='DNSCryptoKey', + name="DNSCryptoKey", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, - auto_created=True, primary_key=True)), - ('flags', models.IntegerField()), - ('active', models.BooleanField(default=True)), - ('content', models.TextField()), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("flags", models.IntegerField()), + ("active", models.BooleanField(default=True)), + ("content", models.TextField()), ], ), migrations.CreateModel( - name='DNSDomain', + name="DNSDomain", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, - auto_created=True, primary_key=True)), - ('created', model_utils.fields.AutoCreatedField( - default=django.utils.timezone.now, verbose_name='created', - editable=False)), - ('modified', model_utils.fields.AutoLastModifiedField( - default=django.utils.timezone.now, verbose_name='modified', - editable=False)), - ('domain', models.CharField( - unique=True, max_length=255, verbose_name='domain name')), - ('master', - models.CharField(max_length=128, null=True, blank=True)), - ('last_check', models.IntegerField(null=True)), - ('domaintype', models.CharField( - max_length=6, db_column='type', - choices=[('MASTER', 'Master'), - ('SLAVE', 'Slave'), - ('NATIVE', 'Native')])), - ('notified_serial', models.IntegerField(null=True)), - ('customer', models.ForeignKey( - verbose_name='customer', blank=True, - to=settings.AUTH_USER_MODEL, null=True, - on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + verbose_name="created", + editable=False, + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + verbose_name="modified", + editable=False, + ), + ), + ( + "domain", + models.CharField( + unique=True, max_length=255, verbose_name="domain name" + ), + ), + ("master", models.CharField(max_length=128, null=True, blank=True)), + ("last_check", models.IntegerField(null=True)), + ( + "domaintype", + models.CharField( + max_length=6, + db_column="type", + choices=[ + ("MASTER", "Master"), + ("SLAVE", "Slave"), + ("NATIVE", "Native"), + ], + ), + ), + ("notified_serial", models.IntegerField(null=True)), + ( + "customer", + models.ForeignKey( + verbose_name="customer", + blank=True, + to=settings.AUTH_USER_MODEL, + null=True, + on_delete=models.CASCADE, + ), + ), ], options={ - 'verbose_name': 'DNS domain', - 'verbose_name_plural': 'DNS domains', + "verbose_name": "DNS domain", + "verbose_name_plural": "DNS domains", }, ), migrations.RunSQL( - '''ALTER TABLE domains_dnsdomain ADD CONSTRAINT c_lowercase_name - CHECK (((domain)::TEXT = LOWER((domain)::TEXT)))''' + """ALTER TABLE domains_dnsdomain ADD CONSTRAINT c_lowercase_name + CHECK (((domain)::TEXT = LOWER((domain)::TEXT)))""" ), migrations.CreateModel( - name='DNSDomainMetadata', + name="DNSDomainMetadata", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, - auto_created=True, primary_key=True)), - ('kind', models.CharField(max_length=32)), - ('content', models.TextField()), - ('domain', models.ForeignKey( - to='domains.DNSDomain', on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("kind", models.CharField(max_length=32)), + ("content", models.TextField()), + ( + "domain", + models.ForeignKey(to="domains.DNSDomain", on_delete=models.CASCADE), + ), ], ), migrations.CreateModel( - name='DNSRecord', + name="DNSRecord", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, - auto_created=True, primary_key=True)), - ('name', models.CharField( - db_index=True, max_length=255, null=True, blank=True)), - ('recordtype', models.CharField( - max_length=10, null=True, db_column='type', blank=True)), - ('content', models.CharField( - max_length=65535, null=True, blank=True)), - ('ttl', models.IntegerField(null=True)), - ('prio', models.IntegerField(null=True)), - ('change_date', models.IntegerField(null=True)), - ('disabled', models.BooleanField(default=False)), - ('ordername', models.CharField(max_length=255)), - ('auth', models.BooleanField(default=True)), - ('domain', models.ForeignKey( - to='domains.DNSDomain', on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "name", + models.CharField( + db_index=True, max_length=255, null=True, blank=True + ), + ), + ( + "recordtype", + models.CharField( + max_length=10, null=True, db_column="type", blank=True + ), + ), + ("content", models.CharField(max_length=65535, null=True, blank=True)), + ("ttl", models.IntegerField(null=True)), + ("prio", models.IntegerField(null=True)), + ("change_date", models.IntegerField(null=True)), + ("disabled", models.BooleanField(default=False)), + ("ordername", models.CharField(max_length=255)), + ("auth", models.BooleanField(default=True)), + ( + "domain", + models.ForeignKey(to="domains.DNSDomain", on_delete=models.CASCADE), + ), ], options={ - 'verbose_name': 'DNS record', - 'verbose_name_plural': 'DNS records', + "verbose_name": "DNS record", + "verbose_name_plural": "DNS records", }, ), migrations.RunSQL( - '''ALTER TABLE domains_dnsrecord ADD CONSTRAINT c_lowercase_name - CHECK (((name)::TEXT = LOWER((name)::TEXT)))''' + """ALTER TABLE domains_dnsrecord ADD CONSTRAINT c_lowercase_name + CHECK (((name)::TEXT = LOWER((name)::TEXT)))""" ), migrations.RunSQL( - '''CREATE INDEX recordorder ON domains_dnsrecord (domain_id, - ordername text_pattern_ops)''' + """CREATE INDEX recordorder ON domains_dnsrecord (domain_id, + ordername text_pattern_ops)""" ), migrations.CreateModel( - name='DNSSupermaster', + name="DNSSupermaster", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, - auto_created=True, primary_key=True)), - ('ip', models.GenericIPAddressField()), - ('nameserver', models.CharField(max_length=255)), - ('customer', models.ForeignKey( - verbose_name='customer', to=settings.AUTH_USER_MODEL, - on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("ip", models.GenericIPAddressField()), + ("nameserver", models.CharField(max_length=255)), + ( + "customer", + models.ForeignKey( + verbose_name="customer", + to=settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + ), + ), ], ), migrations.CreateModel( - name='DNSTSIGKey', + name="DNSTSIGKey", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, - auto_created=True, primary_key=True)), - ('name', models.CharField(max_length=255)), - ('algorithm', models.CharField(max_length=50)), - ('secret', models.CharField(max_length=255)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ("name", models.CharField(max_length=255)), + ("algorithm", models.CharField(max_length=50)), + ("secret", models.CharField(max_length=255)), ], ), migrations.RunSQL( - '''ALTER TABLE domains_dnstsigkey ADD CONSTRAINT c_lowercase_name - CHECK (((name)::TEXT = LOWER((name)::TEXT)))''' + """ALTER TABLE domains_dnstsigkey ADD CONSTRAINT c_lowercase_name + CHECK (((name)::TEXT = LOWER((name)::TEXT)))""" ), migrations.AlterField( - model_name='hostingdomain', - name='domain', + model_name="hostingdomain", + name="domain", field=models.CharField( - unique=True, max_length=255, verbose_name='domain name'), + unique=True, max_length=255, verbose_name="domain name" + ), ), migrations.AlterField( - model_name='maildomain', - name='domain', + model_name="maildomain", + name="domain", field=models.CharField( - unique=True, max_length=255, verbose_name='domain name'), + unique=True, max_length=255, verbose_name="domain name" + ), ), migrations.AddField( - model_name='dnscryptokey', - name='domain', - field=models.ForeignKey( - to='domains.DNSDomain', on_delete=models.CASCADE), + model_name="dnscryptokey", + name="domain", + field=models.ForeignKey(to="domains.DNSDomain", on_delete=models.CASCADE), ), migrations.AddField( - model_name='dnscomment', - name='domain', - field=models.ForeignKey( - to='domains.DNSDomain', on_delete=models.CASCADE), + model_name="dnscomment", + name="domain", + field=models.ForeignKey(to="domains.DNSDomain", on_delete=models.CASCADE), ), migrations.AlterUniqueTogether( - name='dnssupermaster', - unique_together=set([('ip', 'nameserver')]), + name="dnssupermaster", + unique_together=set([("ip", "nameserver")]), ), migrations.AlterUniqueTogether( - name='dnstsigkey', - unique_together=set([('name', 'algorithm')]), + name="dnstsigkey", + unique_together=set([("name", "algorithm")]), ), migrations.AlterIndexTogether( - name='dnsrecord', - index_together=set([('name', 'recordtype')]), + name="dnsrecord", + index_together=set([("name", "recordtype")]), ), migrations.AlterIndexTogether( - name='dnscomment', - index_together={('name', 'commenttype'), ('domain', 'modified_at')}, + name="dnscomment", + index_together={("name", "commenttype"), ("domain", "modified_at")}, ), ] diff --git a/gnuviechadmin/domains/migrations/0004_auto_20151107_1708.py b/gnuviechadmin/domains/migrations/0004_auto_20151107_1708.py index 40d407d..686126c 100644 --- a/gnuviechadmin/domains/migrations/0004_auto_20151107_1708.py +++ b/gnuviechadmin/domains/migrations/0004_auto_20151107_1708.py @@ -1,44 +1,87 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('domains', '0003_auto_20151105_2133'), + ("domains", "0003_auto_20151105_2133"), ] operations = [ migrations.AlterModelOptions( - name='dnscomment', - options={'verbose_name': 'DNS comment', 'verbose_name_plural': 'DNS comments'}, + name="dnscomment", + options={ + "verbose_name": "DNS comment", + "verbose_name_plural": "DNS comments", + }, ), migrations.AlterModelOptions( - name='dnscryptokey', - options={'verbose_name': 'DNS crypto key', 'verbose_name_plural': 'DNS crypto keys'}, + name="dnscryptokey", + options={ + "verbose_name": "DNS crypto key", + "verbose_name_plural": "DNS crypto keys", + }, ), migrations.AlterModelOptions( - name='dnsdomainmetadata', - options={'verbose_name': 'DNS domain metadata item', 'verbose_name_plural': 'DNS domain metadata items'}, + name="dnsdomainmetadata", + options={ + "verbose_name": "DNS domain metadata item", + "verbose_name_plural": "DNS domain metadata items", + }, ), migrations.AlterModelOptions( - name='dnssupermaster', - options={'verbose_name': 'DNS supermaster', 'verbose_name_plural': 'DNS supermasters'}, + name="dnssupermaster", + options={ + "verbose_name": "DNS supermaster", + "verbose_name_plural": "DNS supermasters", + }, ), migrations.AlterModelOptions( - name='dnstsigkey', - options={'verbose_name': 'DNS TSIG key', 'verbose_name_plural': 'DNS TSIG keys'}, + name="dnstsigkey", + options={ + "verbose_name": "DNS TSIG key", + "verbose_name_plural": "DNS TSIG keys", + }, ), migrations.AlterField( - model_name='dnsdomainmetadata', - name='kind', - field=models.CharField(max_length=32, choices=[('ALLOW-DNSUPDATE-FROM', 'ALLOW-DNSUPDATE-FROM'), ('ALSO-NOTIFY', 'ALSO-NOTIFY'), ('AXFR-MASTER-TSIG', 'AXFR-MASTER-TSIG'), ('AXFR-SOURCE', 'AXFR-SOURCE'), ('FORWARD-DNSUPDATE', 'FORWARD-DNSUPDATE'), ('GSS-ACCEPTOR-PRINCIPAL', 'GSS-ACCEPTOR-PRINCIPAL'), ('GSS-ALLOW-AXFR-PRINCIPAL', 'GSS-ALLOW-AXFR-PRINCIPAL'), ('LUA-AXFR-SCRIPT', 'LUA-AXFR-SCRIPT'), ('NSEC3NARROW', 'NSEC3NARROW'), ('NSEC3PARAM', 'NSEC3PARAM'), ('PRESIGNED', 'PRESIGNED'), ('PUBLISH_CDNSKEY', 'PUBLISH_CDNSKEY'), ('PUBLISH_CDS', 'PUBLISH_CDS'), ('SOA-EDIT', 'SOA-EDIT'), ('SOA-EDIT-DNSUPDATE', 'SOA-EDIT-DNSUPDATE'), ('TSIG-ALLOW-AXFR', 'TSIG-ALLOW-AXFR'), ('TSIG-ALLOW-DNSUPDATE', 'TSIG-ALLOW-DNSUPDATE')]), + model_name="dnsdomainmetadata", + name="kind", + field=models.CharField( + max_length=32, + choices=[ + ("ALLOW-DNSUPDATE-FROM", "ALLOW-DNSUPDATE-FROM"), + ("ALSO-NOTIFY", "ALSO-NOTIFY"), + ("AXFR-MASTER-TSIG", "AXFR-MASTER-TSIG"), + ("AXFR-SOURCE", "AXFR-SOURCE"), + ("FORWARD-DNSUPDATE", "FORWARD-DNSUPDATE"), + ("GSS-ACCEPTOR-PRINCIPAL", "GSS-ACCEPTOR-PRINCIPAL"), + ("GSS-ALLOW-AXFR-PRINCIPAL", "GSS-ALLOW-AXFR-PRINCIPAL"), + ("LUA-AXFR-SCRIPT", "LUA-AXFR-SCRIPT"), + ("NSEC3NARROW", "NSEC3NARROW"), + ("NSEC3PARAM", "NSEC3PARAM"), + ("PRESIGNED", "PRESIGNED"), + ("PUBLISH_CDNSKEY", "PUBLISH_CDNSKEY"), + ("PUBLISH_CDS", "PUBLISH_CDS"), + ("SOA-EDIT", "SOA-EDIT"), + ("SOA-EDIT-DNSUPDATE", "SOA-EDIT-DNSUPDATE"), + ("TSIG-ALLOW-AXFR", "TSIG-ALLOW-AXFR"), + ("TSIG-ALLOW-DNSUPDATE", "TSIG-ALLOW-DNSUPDATE"), + ], + ), ), migrations.AlterField( - model_name='dnstsigkey', - name='algorithm', - field=models.CharField(max_length=50, choices=[('hmac-md5', 'HMAC MD5'), ('hmac-sha1', 'HMAC SHA1'), ('hmac-sha224', 'HMAC SHA224'), ('hmac-sha256', 'HMAC SHA256'), ('hmac-sha384', 'HMAC SHA384'), ('hmac-sha512', 'HMAC SHA512')]), + model_name="dnstsigkey", + name="algorithm", + field=models.CharField( + max_length=50, + choices=[ + ("hmac-md5", "HMAC MD5"), + ("hmac-sha1", "HMAC SHA1"), + ("hmac-sha224", "HMAC SHA224"), + ("hmac-sha256", "HMAC SHA256"), + ("hmac-sha384", "HMAC SHA384"), + ("hmac-sha512", "HMAC SHA512"), + ], + ), ), ] diff --git a/gnuviechadmin/domains/migrations/0005_remove_unused_pdns_tables.py b/gnuviechadmin/domains/migrations/0005_remove_unused_pdns_tables.py new file mode 100644 index 0000000..87e9c32 --- /dev/null +++ b/gnuviechadmin/domains/migrations/0005_remove_unused_pdns_tables.py @@ -0,0 +1,74 @@ +# Generated by Django 3.2.18 on 2023-04-15 09:53 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('domains', '0004_auto_20151107_1708'), + ] + + operations = [ + migrations.AlterIndexTogether( + name='dnscomment', + index_together=None, + ), + migrations.RemoveField( + model_name='dnscomment', + name='customer', + ), + migrations.RemoveField( + model_name='dnscomment', + name='domain', + ), + migrations.RemoveField( + model_name='dnscryptokey', + name='domain', + ), + migrations.RemoveField( + model_name='dnsdomain', + name='customer', + ), + migrations.RemoveField( + model_name='dnsdomainmetadata', + name='domain', + ), + migrations.AlterIndexTogether( + name='dnsrecord', + index_together=None, + ), + migrations.RemoveField( + model_name='dnsrecord', + name='domain', + ), + migrations.AlterUniqueTogether( + name='dnssupermaster', + unique_together=None, + ), + migrations.RemoveField( + model_name='dnssupermaster', + name='customer', + ), + migrations.DeleteModel( + name='DNSTSIGKey', + ), + migrations.DeleteModel( + name='DNSComment', + ), + migrations.DeleteModel( + name='DNSCryptoKey', + ), + migrations.DeleteModel( + name='DNSDomain', + ), + migrations.DeleteModel( + name='DNSDomainMetadata', + ), + migrations.DeleteModel( + name='DNSRecord', + ), + migrations.DeleteModel( + name='DNSSupermaster', + ), + ] diff --git a/gnuviechadmin/domains/models.py b/gnuviechadmin/domains/models.py index 4fff880..7332dd6 100644 --- a/gnuviechadmin/domains/models.py +++ b/gnuviechadmin/domains/models.py @@ -2,52 +2,12 @@ This module contains models related to domain names. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import -from django.db import models, transaction from django.conf import settings -from django.utils.encoding import python_2_unicode_compatible -from django.utils.translation import ugettext as _ - +from django.db import models, transaction +from django.utils.translation import gettext as _ from model_utils.models import TimeStampedModel -from model_utils import Choices - - -DNS_DOMAIN_TYPES = Choices( - ('MASTER', _('Master')), - ('SLAVE', _('Slave')), - ('NATIVE', _('Native')), -) - -# see https://doc.powerdns.com/md/authoritative/domainmetadata/ -DNS_DOMAIN_METADATA_KINDS = Choices( - 'ALLOW-DNSUPDATE-FROM', - 'ALSO-NOTIFY', - 'AXFR-MASTER-TSIG', - 'AXFR-SOURCE', - 'FORWARD-DNSUPDATE', - 'GSS-ACCEPTOR-PRINCIPAL', - 'GSS-ALLOW-AXFR-PRINCIPAL', - 'LUA-AXFR-SCRIPT', - 'NSEC3NARROW', - 'NSEC3PARAM', - 'PRESIGNED', - 'PUBLISH_CDNSKEY', - 'PUBLISH_CDS', - 'SOA-EDIT', - 'SOA-EDIT-DNSUPDATE', - 'TSIG-ALLOW-AXFR', - 'TSIG-ALLOW-DNSUPDATE', -) - -DNS_TSIG_KEY_ALGORITHMS = Choices( - ('hmac-md5', _('HMAC MD5')), - ('hmac-sha1', _('HMAC SHA1')), - ('hmac-sha224', _('HMAC SHA224')), - ('hmac-sha256', _('HMAC SHA256')), - ('hmac-sha384', _('HMAC SHA384')), - ('hmac-sha512', _('HMAC SHA512')), -) class DomainBase(TimeStampedModel): @@ -55,16 +15,20 @@ class DomainBase(TimeStampedModel): This is the base model for domains. """ - domain = models.CharField(_('domain name'), max_length=255, unique=True) + + domain = models.CharField(_("domain name"), max_length=255, unique=True) customer = models.ForeignKey( - settings.AUTH_USER_MODEL, verbose_name=_('customer'), blank=True, - null=True, on_delete=models.CASCADE) + settings.AUTH_USER_MODEL, + verbose_name=_("customer"), + blank=True, + null=True, + on_delete=models.CASCADE, + ) class Meta: abstract = True -@python_2_unicode_compatible class MailDomain(DomainBase): """ This is the model for mail domains. Mail domains are used to configure the @@ -72,9 +36,10 @@ class MailDomain(DomainBase): domains. """ - class Meta: - verbose_name = _('Mail domain') - verbose_name_plural = _('Mail domains') + + class Meta(DomainBase.Meta): + verbose_name = _("Mail domain") + verbose_name_plural = _("Mail domains") def __str__(self): return self.domain @@ -85,6 +50,7 @@ class MailDomain(DomainBase): """ return self.mailaddress_set.all() + mailaddresses = property(get_mailaddresses) @@ -93,339 +59,47 @@ class HostingDomainManager(models.Manager): Default Manager for :py:class:`HostingDomain`. """ + @transaction.atomic - def create_for_hosting_package( - self, hosting_package, domain, commit, **kwargs - ): + def create_for_hosting_package(self, hosting_package, domain, commit, **kwargs): from hostingpackages.models import CustomerHostingPackageDomain + hostingdomain = self.create( - customer=hosting_package.customer, domain=domain, **kwargs) + customer=hosting_package.customer, domain=domain, **kwargs + ) hostingdomain.maildomain = MailDomain.objects.create( - customer=hosting_package.customer, domain=domain) + customer=hosting_package.customer, domain=domain + ) custdomain = CustomerHostingPackageDomain.objects.create( - hosting_package=hosting_package, domain=hostingdomain) + hosting_package=hosting_package, domain=hostingdomain + ) if commit: hostingdomain.save() custdomain.save() return hostingdomain -@python_2_unicode_compatible class HostingDomain(DomainBase): """ This is the model for hosting domains. A hosting domain is linked to a customer hosting account. """ + maildomain = models.OneToOneField( - MailDomain, verbose_name=_('mail domain'), blank=True, null=True, - help_text=_('assigned mail domain for this domain'), + MailDomain, + verbose_name=_("mail domain"), + blank=True, + null=True, + help_text=_("assigned mail domain for this domain"), on_delete=models.CASCADE, ) objects = HostingDomainManager() class Meta: - verbose_name = _('Hosting domain') - verbose_name_plural = _('Hosting domains') + verbose_name = _("Hosting domain") + verbose_name_plural = _("Hosting domains") def __str__(self): return self.domain - - -@python_2_unicode_compatible -class DNSDomain(DomainBase): - """ - This model represents a DNS zone. The model is similar to the domain table - in the PowerDNS schema specified in - https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. - - .. code-block:: sql - - CREATE TABLE domains ( - id SERIAL PRIMARY KEY, - name VARCHAR(255) NOT NULL, - master VARCHAR(128) DEFAULT NULL, - last_check INT DEFAULT NULL, - type VARCHAR(6) NOT NULL, - notified_serial INT DEFAULT NULL, - account VARCHAR(40) DEFAULT NULL, - CONSTRAINT c_lowercase_name CHECK ( - ((name)::TEXT = LOWER((name)::TEXT))) - ); - - CREATE UNIQUE INDEX name_index ON domains(name); - - """ - # name is represented by domain - master = models.CharField(max_length=128, blank=True, null=True) - last_check = models.IntegerField(null=True) - domaintype = models.CharField( - max_length=6, choices=DNS_DOMAIN_TYPES, db_column='type') - notified_serial = models.IntegerField(null=True) - # account is represented by customer_id - # check constraint is added via RunSQL in migration - - class Meta: - verbose_name = _('DNS domain') - verbose_name_plural = _('DNS domains') - - def __str__(self): - return self.domain - - -@python_2_unicode_compatible -class DNSRecord(models.Model): - """ - This model represents a DNS record. The model is similar to the record - table in the PowerDNS schema specified in - https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. - - .. code-block:: sql - - CREATE TABLE records ( - id SERIAL PRIMARY KEY, - domain_id INT DEFAULT NULL, - name VARCHAR(255) DEFAULT NULL, - type VARCHAR(10) DEFAULT NULL, - content VARCHAR(65535) DEFAULT NULL, - ttl INT DEFAULT NULL, - prio INT DEFAULT NULL, - change_date INT DEFAULT NULL, - disabled BOOL DEFAULT 'f', - ordername VARCHAR(255), - auth BOOL DEFAULT 't', - CONSTRAINT domain_exists - FOREIGN KEY(domain_id) REFERENCES domains(id) - ON DELETE CASCADE, - CONSTRAINT c_lowercase_name CHECK ( - ((name)::TEXT = LOWER((name)::TEXT))) - ); - - CREATE INDEX rec_name_index ON records(name); - CREATE INDEX nametype_index ON records(name,type); - CREATE INDEX domain_id ON records(domain_id); - CREATE INDEX recordorder ON records ( - domain_id, ordername text_pattern_ops); - - """ - domain = models.ForeignKey('DNSDomain', on_delete=models.CASCADE) - name = models.CharField( - max_length=255, blank=True, null=True, db_index=True) - recordtype = models.CharField( - max_length=10, blank=True, null=True, db_column='type') - content = models.CharField(max_length=65535, blank=True, null=True) - ttl = models.IntegerField(null=True) - prio = models.IntegerField(null=True) - change_date = models.IntegerField(null=True) - disabled = models.BooleanField(default=False) - ordername = models.CharField(max_length=255) - auth = models.BooleanField(default=True) - # check constraint and index recordorder are added via RunSQL in migration - - class Meta: - verbose_name = _('DNS record') - verbose_name_plural = _('DNS records') - index_together = [ - ['name', 'recordtype'] - ] - - def __str__(self): - return "{name} IN {type} {content}".format( - name=self.name, type=self.recordtype, content=self.content) - - -@python_2_unicode_compatible -class DNSSupermaster(models.Model): - """ - This model represents the supermasters table in the PowerDNS schema - specified in - https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. - - .. code-block:: sql - - CREATE TABLE supermasters ( - ip INET NOT NULL, - nameserver VARCHAR(255) NOT NULL, - account VARCHAR(40) NOT NULL, - PRIMARY KEY(ip, nameserver) - ); - - """ - ip = models.GenericIPAddressField() - nameserver = models.CharField(max_length=255) - # account is replaced by customer - customer = models.ForeignKey( - settings.AUTH_USER_MODEL, verbose_name=_('customer'), - on_delete=models.CASCADE) - - class Meta: - verbose_name = _('DNS supermaster') - verbose_name_plural = _('DNS supermasters') - unique_together = ( - ('ip', 'nameserver') - ) - - def __str__(self): - return "{ip} {nameserver}".format( - ip=self.ip, nameserver=self.nameserver) - - -@python_2_unicode_compatible -class DNSComment(models.Model): - """ - This model represents the comments table in the PowerDNS schema specified - in https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. The - comments table is used to store user comments related to individual DNS - records. - - .. code-block:: sql - - CREATE TABLE comments ( - id SERIAL PRIMARY KEY, - domain_id INT NOT NULL, - name VARCHAR(255) NOT NULL, - type VARCHAR(10) NOT NULL, - modified_at INT NOT NULL, - account VARCHAR(40) DEFAULT NULL, - comment VARCHAR(65535) NOT NULL, - CONSTRAINT domain_exists - FOREIGN KEY(domain_id) REFERENCES domains(id) - ON DELETE CASCADE, - CONSTRAINT c_lowercase_name CHECK ( - ((name)::TEXT = LOWER((name)::TEXT))) - ); - - CREATE INDEX comments_domain_id_idx ON comments (domain_id); - CREATE INDEX comments_name_type_idx ON comments (name, type); - CREATE INDEX comments_order_idx ON comments (domain_id, modified_at); - - """ - domain = models.ForeignKey('DNSDomain', on_delete=models.CASCADE) - name = models.CharField(max_length=255) - commenttype = models.CharField(max_length=10, db_column='type') - modified_at = models.IntegerField() - # account is replaced by customer - customer = models.ForeignKey( - settings.AUTH_USER_MODEL, verbose_name=_('customer'), - on_delete=models.CASCADE) - comment = models.CharField(max_length=65535) - # check constraint is added via RunSQL in migration - - class Meta: - verbose_name = _('DNS comment') - verbose_name_plural = _('DNS comments') - index_together = [ - ['name', 'commenttype'], - ['domain', 'modified_at'] - ] - - def __str__(self): - return "{name} IN {type}: {comment}".format( - name=self.name, type=self.commenttype, comment=self.comment) - - -@python_2_unicode_compatible -class DNSDomainMetadata(models.Model): - """ - This model represents the domainmetadata table in the PowerDNS schema - specified in - https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. - The domainmetadata table is used to store domain meta data as described in - https://doc.powerdns.com/md/authoritative/domainmetadata/. - - .. code-block:: sql - - CREATE TABLE domainmetadata ( - id SERIAL PRIMARY KEY, - domain_id INT REFERENCES domains(id) ON DELETE CASCADE, - kind VARCHAR(32), - content TEXT - ); - - CREATE INDEX domainidmetaindex ON domainmetadata(domain_id); - - """ - domain = models.ForeignKey('DNSDomain', on_delete=models.CASCADE) - kind = models.CharField(max_length=32, choices=DNS_DOMAIN_METADATA_KINDS) - content = models.TextField() - - class Meta: - verbose_name = _('DNS domain metadata item') - verbose_name_plural = _('DNS domain metadata items') - - def __str__(self): - return "{domain} {kind} {content}".format( - domain=self.domain.domain, kind=self.kind, content=self.content) - - -@python_2_unicode_compatible -class DNSCryptoKey(models.Model): - """ - This model represents the cryptokeys table in the PowerDNS schema - specified in - https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. - - .. code-block:: sql - - CREATE TABLE cryptokeys ( - id SERIAL PRIMARY KEY, - domain_id INT REFERENCES domains(id) ON DELETE CASCADE, - flags INT NOT NULL, - active BOOL, - content TEXT - ); - - CREATE INDEX domainidindex ON cryptokeys(domain_id); - - """ - domain = models.ForeignKey('DNSDomain', on_delete=models.CASCADE) - flags = models.IntegerField() - active = models.BooleanField(default=True) - content = models.TextField() - - class Meta: - verbose_name = _('DNS crypto key') - verbose_name_plural = _('DNS crypto keys') - - def __str__(self): - return "{domain} {content}".format( - domain=self.domain.domain, content=self.content) - - -@python_2_unicode_compatible -class DNSTSIGKey(models.Model): - """ - This model represents the tsigkeys table in the PowerDNS schema specified - in https://doc.powerdns.com/md/authoritative/backend-generic-mypgsql/. - - .. code-block:: sql - - CREATE TABLE tsigkeys ( - id SERIAL PRIMARY KEY, - name VARCHAR(255), - algorithm VARCHAR(50), - secret VARCHAR(255), - CONSTRAINT c_lowercase_name CHECK ( - ((name)::TEXT = LOWER((name)::TEXT))) - ); - - CREATE UNIQUE INDEX namealgoindex ON tsigkeys(name, algorithm); - - """ - name = models.CharField(max_length=255) - algorithm = models.CharField( - max_length=50, choices=DNS_TSIG_KEY_ALGORITHMS) - secret = models.CharField(max_length=255) - # check constraint is added via RunSQL in migration - - class Meta: - verbose_name = _('DNS TSIG key') - verbose_name_plural = _('DNS TSIG keys') - unique_together = [ - ['name', 'algorithm'] - ] - - def __str__(self): - return "{name} {algorithm} XXXX".format( - name=self.name, algorithm=self.algorithm) diff --git a/gnuviechadmin/domains/tests/test_forms.py b/gnuviechadmin/domains/tests/test_forms.py index 7bc1f64..3e21931 100644 --- a/gnuviechadmin/domains/tests/test_forms.py +++ b/gnuviechadmin/domains/tests/test_forms.py @@ -7,9 +7,9 @@ from unittest.mock import MagicMock, Mock, patch from django.forms import ValidationError from django.test import TestCase from django.urls import reverse -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext as _ -from domains.forms import relative_domain_validator, CreateHostingDomainForm +from domains.forms import CreateHostingDomainForm, relative_domain_validator class RelativeDomainValidatorTest(TestCase): diff --git a/gnuviechadmin/domains/tests/test_models.py b/gnuviechadmin/domains/tests/test_models.py index fd6d645..ef3b0d8 100644 --- a/gnuviechadmin/domains/tests/test_models.py +++ b/gnuviechadmin/domains/tests/test_models.py @@ -7,19 +7,9 @@ from unittest.mock import patch from django.test import TestCase from django.contrib.auth import get_user_model -from domains.models import ( - DNSComment, - DNSCryptoKey, - DNSDomain, - DNSDomainMetadata, - DNSRecord, - DNSSupermaster, - DNSTSIGKey, - HostingDomain, - MailDomain, -) -from hostingpackages.models import CustomerHostingPackage, HostingPackageTemplate +from domains.models import HostingDomain, MailDomain +from hostingpackages.models import CustomerHostingPackage, HostingPackageTemplate User = get_user_model() @@ -77,49 +67,3 @@ class HostingDomainTest(TestCase): def test___str__(self): hostingdomain = HostingDomain(domain="test") self.assertEqual(str(hostingdomain), "test") - - -class DNSDomainTest(TestCase): - def test___str__(self): - dnsdomain = DNSDomain(domain="test") - self.assertEqual(str(dnsdomain), "test") - - -class DNSRecordTest(TestCase): - def test___str__(self): - dnsrecord = DNSRecord(name="localhost", recordtype="A", content="127.0.0.1") - self.assertEqual(str(dnsrecord), "localhost IN A 127.0.0.1") - - -class DNSSupermasterTest(TestCase): - def test___str__(self): - dnssupermaster = DNSSupermaster(ip="127.0.0.1", nameserver="dns.example.org") - self.assertEqual(str(dnssupermaster), "127.0.0.1 dns.example.org") - - -class DNSCommentTest(TestCase): - def test___str__(self): - dnscomment = DNSComment(name="localhost", commenttype="A", comment="good stuff") - self.assertEqual(str(dnscomment), "localhost IN A: good stuff") - - -class DNSDomainMetadataTest(TestCase): - def test___str__(self): - dnsdomain = DNSDomain(domain="test") - dnsdomainmetadata = DNSDomainMetadata( - domain=dnsdomain, kind="SOA-EDIT", content="INCEPTION" - ) - self.assertEqual(str(dnsdomainmetadata), "test SOA-EDIT INCEPTION") - - -class DNSCryptoKeyTest(TestCase): - def test___str__(self): - dnsdomain = DNSDomain(domain="test") - dnscryptokey = DNSCryptoKey(domain=dnsdomain, content="testvalue") - self.assertEqual(str(dnscryptokey), "test testvalue") - - -class DNSTSIGKeyTest(TestCase): - def test___str__(self): - dnstsigkey = DNSTSIGKey(name="testkey", algorithm="hmac-md5", secret="dummykey") - self.assertEqual(str(dnstsigkey), "testkey hmac-md5 XXXX") diff --git a/gnuviechadmin/domains/urls.py b/gnuviechadmin/domains/urls.py index 2d63fe4..20c1557 100644 --- a/gnuviechadmin/domains/urls.py +++ b/gnuviechadmin/domains/urls.py @@ -2,14 +2,16 @@ This module defines the URL patterns for domain related views. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import -from django.conf.urls import url +from django.urls import re_path from .views import CreateHostingDomain - urlpatterns = [ - url(r'^(?P\d+)/create$', CreateHostingDomain.as_view(), - name='create_hosting_domain'), + re_path( + r"^(?P\d+)/create$", + CreateHostingDomain.as_view(), + name="create_hosting_domain", + ), ] diff --git a/gnuviechadmin/domains/views.py b/gnuviechadmin/domains/views.py index 41ca975..ac387ec 100644 --- a/gnuviechadmin/domains/views.py +++ b/gnuviechadmin/domains/views.py @@ -2,20 +2,21 @@ This module defines views related to domains. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import -from braces.views import StaffuserRequiredMixin from django.contrib import messages +from django.contrib.auth.mixins import PermissionRequiredMixin from django.shortcuts import get_object_or_404, redirect -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext as _ from django.views.generic.edit import CreateView from hostingpackages.models import CustomerHostingPackage + from .forms import CreateHostingDomainForm from .models import HostingDomain -class CreateHostingDomain(StaffuserRequiredMixin, CreateView): +class CreateHostingDomain(PermissionRequiredMixin, CreateView): """ This view is used for creating a new HostingDomain instance for an existing hosting package. @@ -23,6 +24,7 @@ class CreateHostingDomain(StaffuserRequiredMixin, CreateView): model = HostingDomain raise_exception = True + permission_required = 'domains.add_hostingdomain' template_name_suffix = "_create" form_class = CreateHostingDomainForm diff --git a/gnuviechadmin/gnuviechadmin/__init__.py b/gnuviechadmin/gnuviechadmin/__init__.py index 1fd249f..7dfb6e8 100644 --- a/gnuviechadmin/gnuviechadmin/__init__.py +++ b/gnuviechadmin/gnuviechadmin/__init__.py @@ -1,4 +1,4 @@ # import celery_app to initialize it from gnuviechadmin.celery import app as celery_app # NOQA -__version__ = '0.12.1' +__version__ = "0.13.0" diff --git a/gnuviechadmin/gnuviechadmin/auth.py b/gnuviechadmin/gnuviechadmin/auth.py new file mode 100644 index 0000000..f493471 --- /dev/null +++ b/gnuviechadmin/gnuviechadmin/auth.py @@ -0,0 +1,11 @@ +from allauth.account.adapter import DefaultAccountAdapter + + +class NoNewUsersAccountAdapter(DefaultAccountAdapter): + """ + Adapter to disable allauth new signups + + """ + + def is_open_for_signup(self, request): + return False diff --git a/gnuviechadmin/gnuviechadmin/celery.py b/gnuviechadmin/gnuviechadmin/celery.py index b9a95d2..02781d2 100644 --- a/gnuviechadmin/gnuviechadmin/celery.py +++ b/gnuviechadmin/gnuviechadmin/celery.py @@ -6,15 +6,15 @@ from celery import Celery from django.conf import settings -os.environ.setdefault('DJANGO_SETTINGS_MODULE', - 'gnuviechadmin.settings.production') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gnuviechadmin.settings") -app = Celery('gnuviechadmin') +app = Celery("gnuviechadmin") def get_installed_apps(): return settings.INSTALLED_APPS -app.config_from_object('django.conf:settings') + +app.config_from_object("django.conf:settings") app.autodiscover_tasks(get_installed_apps) diff --git a/gnuviechadmin/gnuviechadmin/context_processors.py b/gnuviechadmin/gnuviechadmin/context_processors.py index 09f1226..4f17f19 100644 --- a/gnuviechadmin/gnuviechadmin/context_processors.py +++ b/gnuviechadmin/gnuviechadmin/context_processors.py @@ -2,7 +2,7 @@ This module provides context processor implementations for gnuviechadmin. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import import logging @@ -22,38 +22,42 @@ def navigation(request): :rtype: dict """ - if request.is_ajax(): + if request.headers.get("x-requested-with") == "XMLHttpRequest": return {} context = { - 'webmail_url': settings.GVA_LINK_WEBMAIL, - 'phpmyadmin_url': settings.GVA_LINK_PHPMYADMIN, - 'phppgadmin_url': settings.GVA_LINK_PHPPGADMIN, - 'active_item': 'dashboard', + "webmail_url": settings.GVA_LINK_WEBMAIL, + "phpmyadmin_url": settings.GVA_LINK_PHPMYADMIN, + "phppgadmin_url": settings.GVA_LINK_PHPPGADMIN, + "active_item": "dashboard", } if request.resolver_match: viewfunc = request.resolver_match.func viewmodule = viewfunc.__module__ - if viewmodule == 'contact_form.views': - context['active_item'] = 'contact' + if viewmodule == "contact_form.views": + context["active_item"] = "contact" elif viewmodule in ( - 'hostingpackages.views', 'osusers.views', 'userdbs.views', - 'managemails.views', 'websites.views', 'domains.views', + "hostingpackages.views", + "osusers.views", + "userdbs.views", + "managemails.views", + "websites.views", + "domains.views", ): - context['active_item'] = 'hostingpackage' - elif viewmodule in ( - 'allauth.account.views', 'allauth.socialaccount.views' + context["active_item"] = "hostingpackage" + elif viewmodule in ("allauth.account.views", "allauth.socialaccount.views"): + context["active_item"] = "account" + elif viewmodule == "django.contrib.flatpages.views" and request.path.endswith( + "/impressum/" ): - context['active_item'] = 'account' - elif ( - viewmodule == 'django.contrib.flatpages.views' and - request.path.endswith('/impressum/') - ): - context['active_item'] = 'imprint' - elif not viewmodule.startswith('django.contrib.admin'): + context["active_item"] = "imprint" + elif not viewmodule.startswith("django.contrib.admin"): _LOGGER.debug( - 'no special handling for view %s in module %s, fallback to ' - 'default active menu item %s', - viewfunc.__name__, viewmodule, context['active_item']) + "no special handling for view %s in module %s, fallback to " + "default active menu item %s", + viewfunc.__name__, + viewmodule, + context["active_item"], + ) return context @@ -64,6 +68,6 @@ def version_info(request): """ context = { - 'gnuviechadmin_version': gvaversion, + "gnuviechadmin_version": gvaversion, } return context diff --git a/gnuviechadmin/gnuviechadmin/settings.py b/gnuviechadmin/gnuviechadmin/settings.py index 0d9ee46..9aca17b 100644 --- a/gnuviechadmin/gnuviechadmin/settings.py +++ b/gnuviechadmin/gnuviechadmin/settings.py @@ -8,10 +8,8 @@ Common settings and globals. from os.path import abspath, basename, dirname, join, normpath from django.contrib.messages import constants as messages - from gvacommon.settings_utils import get_env_variable - # ######### PATH CONFIGURATION # Absolute filesystem path to the Django project directory: DJANGO_ROOT = dirname(dirname(abspath(__file__))) @@ -24,10 +22,11 @@ SITE_NAME = basename(DJANGO_ROOT) # ######### END PATH CONFIGURATION +GVA_ENVIRONMENT = get_env_variable("GVA_ENVIRONMENT", default="prod") # ######### DEBUG CONFIGURATION # See: https://docs.djangoproject.com/en/dev/ref/settings/#debug -DEBUG = False +DEBUG = GVA_ENVIRONMENT == "local" # ######### END DEBUG CONFIGURATION @@ -57,6 +56,8 @@ DATABASES = { "PORT": get_env_variable("GVA_PGSQL_PORT", int, default=5432), } } + +DEFAULT_AUTO_FIELD = "django.db.models.AutoField" # ######### END DATABASE CONFIGURATION @@ -85,7 +86,6 @@ USE_TZ = True LOCALE_PATHS = (normpath(join(SITE_ROOT, "gnuviechadmin", "locale")),) - # ######### MEDIA CONFIGURATION # See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root MEDIA_ROOT = normpath(join(SITE_ROOT, "media")) @@ -179,7 +179,6 @@ AUTHENTICATION_BACKENDS = ( "allauth.account.auth_backends.AuthenticationBackend", ) - # ######### URL CONFIGURATION # See: https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf ROOT_URLCONF = "%s.urls" % SITE_NAME @@ -207,6 +206,10 @@ DJANGO_APPS = ( # Flatpages for about page "django.contrib.flatpages", "crispy_forms", + "crispy_bootstrap5", + "impersonate", + "rest_framework", + "rest_framework.authtoken", ) ALLAUTH_APPS = ( @@ -215,7 +218,6 @@ ALLAUTH_APPS = ( "allauth.socialaccount", "allauth.socialaccount.providers.google", "allauth.socialaccount.providers.linkedin_oauth2", - "allauth.socialaccount.providers.twitter", ) # Apps specific for this project go here. @@ -233,6 +235,8 @@ LOCAL_APPS = ( "userdbs", "hostingpackages", "websites", + "help", + "invoices", "contact_form", ) @@ -250,18 +254,38 @@ MESSAGE_TAGS = { # ######### ALLAUTH CONFIGURATION +ACCOUNT_ADAPTER = "gnuviechadmin.auth.NoNewUsersAccountAdapter" ACCOUNT_EMAIL_REQUIRED = True ACCOUNT_EMAIL_VERIFICATION = "mandatory" LOGIN_REDIRECT_URL = "/" +SOCIALACCOUNT_AUTO_SIGNUP = False SOCIALACCOUNT_QUERY_EMAIL = True # ######### END ALLAUTH CONFIGURATION # ######### CRISPY FORMS CONFIGURATION -CRISPY_TEMPLATE_PACK = "bootstrap3" +CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" +CRISPY_TEMPLATE_PACK = "bootstrap5" # ######### END CRISPY_FORMS CONFIGURATION +# ######### REST FRAMEWORK CONFIGURATION +REST_FRAMEWORK = { + "DEFAULT_AUTHENTICATION_CLASSES": [ + "rest_framework.authentication.BasicAuthentication", + "rest_framework.authentication.SessionAuthentication", + "rest_framework.authentication.TokenAuthentication", + ], + "DEFAULT_RENDERER_CLASSES": [ + "rest_framework.renderers.JSONRenderer", + ], + "DEFAULT_PERMISSION_CLASSES": [ + "rest_framework.permissions.IsAdminUser", + ], +} +# ######### END REST FRAMEWORK CONFIGURATION + + # ######### LOGGING CONFIGURATION # See: https://docs.djangoproject.com/en/dev/ref/settings/#logging # A sample logging configuration. The only tangible logging @@ -281,20 +305,45 @@ LOGGING = { }, "filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}}, "handlers": { + "console": { + "class": "logging.StreamHandler", + }, + "logfile": { + "level": "INFO", + "class": "logging.FileHandler", + "filename": get_env_variable("GVA_LOG_FILE", default="gva.log"), + "formatter": "verbose", + }, "mail_admins": { "level": "ERROR", "filters": ["require_debug_false"], "class": "django.utils.log.AdminEmailHandler", - } + }, + }, + "root": { + "handlers": ["console"], + "level": "WARNING", }, "loggers": { "django.request": { "handlers": ["mail_admins"], "level": "ERROR", "propagate": True, - } + }, + "django": { + "handlers": ["logfile"], + "level": "INFO", + "propagate": False, + }, }, } + +for app in LOCAL_APPS: + LOGGING["loggers"][app] = { + "handlers": ["logfile"], + "level": "INFO", + "propagate": False, + } # ######### END LOGGING CONFIGURATION @@ -351,8 +400,6 @@ GVA_LINK_PHPPGADMIN = get_env_variable( ) # ######### END CUSTOM APP CONFIGURATION -GVA_ENVIRONMENT = get_env_variable("GVA_ENVIRONMENT", default="prod") - # ######### STATIC FILE CONFIGURATION # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root STATIC_ROOT = "/srv/gva/static/" @@ -362,11 +409,22 @@ def show_debug_toolbar(request): return DEBUG and GVA_ENVIRONMENT == "local" -if GVA_ENVIRONMENT == "local": - # ######### DEBUG CONFIGURATION - # See: https://docs.djangoproject.com/en/dev/ref/settings/#debug - DEBUG = True +# ######### TOOLBAR CONFIGURATION +# See: http://django-debug-toolbar.readthedocs.org/en/latest/installation.html#explicit-setup # noqa +INSTALLED_APPS += ("debug_toolbar",) +MIDDLEWARE += [ + "impersonate.middleware.ImpersonateMiddleware", + "debug_toolbar.middleware.DebugToolbarMiddleware", +] + +DEBUG_TOOLBAR_CONFIG = { + "SHOW_TOOLBAR_CALLBACK": "gnuviechadmin.settings.show_debug_toolbar" +} +# ######### END TOOLBAR CONFIGURATION + + +if GVA_ENVIRONMENT == "local": # See: https://docs.djangoproject.com/en/dev/ref/settings/#template-debug TEMPLATES[0]["OPTIONS"]["debug"] = DEBUG # ######### END DEBUG CONFIGURATION @@ -381,12 +439,6 @@ if GVA_ENVIRONMENT == "local": CACHES = {"default": {"BACKEND": "django.core.cache.backends.locmem.LocMemCache"}} # ######### END CACHE CONFIGURATION - # ######### TOOLBAR CONFIGURATION - # See: http://django-debug-toolbar.readthedocs.org/en/latest/installation.html#explicit-setup # noqa - INSTALLED_APPS += ("debug_toolbar",) - - MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] - LOGGING["handlers"].update( { "console": { @@ -400,32 +452,10 @@ if GVA_ENVIRONMENT == "local": dict( [ (key, {"handlers": ["console"], "level": "DEBUG", "propagate": True}) - for key in [ - "dashboard", - "domains", - "fileservertasks", - "gvacommon", - "gvawebcore", - "hostingpackages", - "ldaptasks", - "managemails", - "mysqltasks", - "osusers", - "pgsqltasks", - "taskresults", - "userdbs", - "websites", - ] - ] + for key in LOCAL_APPS + ], ) ) - - DEBUG_TOOLBAR_PATCH_SETTINGS = False - DEBUG_TOOLBAR_CONFIG = { - "SHOW_TOOLBAR_CALLBACK": "gnuviechadmin.settings.show_debug_toolbar" - } - - # ######### END TOOLBAR CONFIGURATION elif GVA_ENVIRONMENT == "test": ALLOWED_HOSTS = ["localhost"] PASSWORD_HASHERS = ("django.contrib.auth.hashers.MD5PasswordHasher",) @@ -442,25 +472,15 @@ elif GVA_ENVIRONMENT == "test": dict( [ (key, {"handlers": ["console"], "level": "ERROR", "propagate": True}) - for key in [ - "dashboard", - "domains", - "fileservertasks", - "gvacommon", - "gvawebcore", - "hostingpackages", - "ldaptasks", - "managemails", - "mysqltasks", - "osusers", - "pgsqltasks", - "taskresults", - "userdbs", - "websites", - ] + for key in LOCAL_APPS ] ) ) + LOGGING["loggers"]["django"] = { + "handlers": ["console"], + "level": "CRITICAL", + "propagate": True, + } BROKER_URL = BROKER_URL + "_test" CELERY_RESULT_PERSISTENT = False else: diff --git a/gnuviechadmin/gnuviechadmin/tests/test_contextprocessors.py b/gnuviechadmin/gnuviechadmin/tests/test_contextprocessors.py index 664e8d2..43b5060 100644 --- a/gnuviechadmin/gnuviechadmin/tests/test_contextprocessors.py +++ b/gnuviechadmin/gnuviechadmin/tests/test_contextprocessors.py @@ -18,13 +18,16 @@ from gnuviechadmin.context_processors import navigation User = get_user_model() +TEST_USER = "test" +TEST_PASSWORD = "secret" + class NavigationContextProcessorTest(TestCase): EXPECTED_ITEMS = ("webmail_url", "phpmyadmin_url", "phppgadmin_url", "active_item") def test_ajax_request(self): - response = self.client.get("/", HTTP_X_REQUESTED_WITH="XMLHttpRequest") + response = self.client.get("/accounts/login/", HTTP_X_REQUESTED_WITH="XMLHttpRequest") for item in self.EXPECTED_ITEMS: self.assertNotIn(item, response.context) @@ -34,6 +37,12 @@ class NavigationContextProcessorTest(TestCase): self.assertEqual(context["phppgadmin_url"], settings.GVA_LINK_PHPPGADMIN) def test_index_page_context(self): + user = User.objects.create(username=TEST_USER) + user.set_password(TEST_PASSWORD) + user.save() + + self.client.login(username=TEST_USER, password=TEST_PASSWORD) + response = self.client.get("/") for item in self.EXPECTED_ITEMS: self.assertIn(item, response.context) @@ -47,15 +56,6 @@ class NavigationContextProcessorTest(TestCase): self._check_static_urls(response.context) self.assertEqual(response.context["active_item"], "contact") - def test_hostingpackage_page_context(self): - User.objects.create_user("test", password="test") - self.client.login(username="test", password="test") - response = self.client.get(reverse("hosting_packages", kwargs={"user": "test"})) - for item in self.EXPECTED_ITEMS: - self.assertIn(item, response.context) - self._check_static_urls(response.context) - self.assertEqual(response.context["active_item"], "hostingpackage") - def _test_page_context_by_viewmodule(self, viewmodule, expecteditem): request = HttpRequest() request.resolver_match = MagicMock() @@ -106,6 +106,6 @@ class NavigationContextProcessorTest(TestCase): class VersionInfoContextProcessorTest(TestCase): def test_version_info_in_context(self): - response = self.client.get("/") + response = self.client.get("/accounts/login/") self.assertIn("gnuviechadmin_version", response.context) self.assertEqual(response.context["gnuviechadmin_version"], gvaversion) diff --git a/gnuviechadmin/gnuviechadmin/urls.py b/gnuviechadmin/gnuviechadmin/urls.py index 2b6d0b1..4253e77 100644 --- a/gnuviechadmin/gnuviechadmin/urls.py +++ b/gnuviechadmin/gnuviechadmin/urls.py @@ -1,36 +1,49 @@ from __future__ import absolute_import -from django.conf.urls import include, url -from django.conf import settings - +import debug_toolbar +from django.conf.urls import include from django.contrib import admin from django.contrib.flatpages import views from django.contrib.staticfiles.urls import staticfiles_urlpatterns +from django.urls import path + +from help import views as help_views +from invoices import views as invoice_views admin.autodiscover() urlpatterns = [ - url(r'', include('dashboard.urls')), - url(r'^accounts/', include('allauth.urls')), - url(r'^database/', include('userdbs.urls')), - url(r'^domains/', include('domains.urls')), - url(r'^hosting/', include('hostingpackages.urls')), - url(r'^website/', include('websites.urls')), - url(r'^mail/', include('managemails.urls')), - url(r'^osuser/', include('osusers.urls')), - url(r'^admin/', admin.site.urls), - url(r'^contact/', include('contact_form.urls')), - url(r'^impressum/$', views.flatpage, { - 'url': '/impressum/' - }, name='imprint'), + path("", include("dashboard.urls")), + path("api/users/", help_views.ListHelpUserAPIView.as_view()), + path( + "api/users//", + help_views.HelpUserAPIView.as_view(), + name="helpuser-detail", + ), + path("api/invoices/", invoice_views.ListInvoiceAPIView.as_view()), + path( + "api/invoices//", + invoice_views.InvoiceAPIView.as_view(), + name="invoice-detail", + ), + path("admin/", admin.site.urls), + path("impersonate/", include("impersonate.urls")), + path("accounts/", include("allauth.urls")), + path("database/", include("userdbs.urls")), + path("domains/", include("domains.urls")), + path("hosting/", include("hostingpackages.urls")), + path("website/", include("websites.urls")), + path("mail/", include("managemails.urls")), + path("osuser/", include("osusers.urls")), + path("contact/", include("contact_form.urls")), + path("impressum/", views.flatpage, {"url": "/impressum/"}, name="imprint"), + path("datenschutz/", views.flatpage, {"url": "/datenschutz/"}, name="privacy"), ] # Uncomment the next line to serve media files in dev. # urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) -if settings.DEBUG: # pragma: no cover - import debug_toolbar - - urlpatterns = [ - url(r'^__debug__/', include(debug_toolbar.urls)), - ] + staticfiles_urlpatterns() + urlpatterns +urlpatterns += staticfiles_urlpatterns() +urlpatterns += [ + path("__debug__/", include(debug_toolbar.urls)), +] diff --git a/gnuviechadmin/gvawebcore/forms.py b/gnuviechadmin/gvawebcore/forms.py index 7a72d83..cdf5426 100644 --- a/gnuviechadmin/gvawebcore/forms.py +++ b/gnuviechadmin/gvawebcore/forms.py @@ -3,11 +3,10 @@ This module defines form classes that can be extended by other gnuviechadmin apps' forms. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import from django import forms -from django.utils.translation import ugettext_lazy as _ - +from django.utils.translation import gettext_lazy as _ PASSWORD_MISMATCH_ERROR = _("Passwords don't match") """ @@ -21,11 +20,14 @@ class PasswordModelFormMixin(forms.Form): whether both fields contain the same string. """ + password1 = forms.CharField( - label=_('Password'), widget=forms.PasswordInput, + label=_("Password"), + widget=forms.PasswordInput, ) password2 = forms.CharField( - label=_('Password (again)'), widget=forms.PasswordInput, + label=_("Password (again)"), + widget=forms.PasswordInput, ) def clean_password2(self): @@ -36,8 +38,8 @@ class PasswordModelFormMixin(forms.Form): :rtype: str or None """ - password1 = self.cleaned_data.get('password1') - password2 = self.cleaned_data.get('password2') + password1 = self.cleaned_data.get("password1") + password2 = self.cleaned_data.get("password2") if password1 and password2 and password1 != password2: raise forms.ValidationError(PASSWORD_MISMATCH_ERROR) return password2 diff --git a/gnuviechadmin/gvawebcore/locale/de/LC_MESSAGES/django.po b/gnuviechadmin/gvawebcore/locale/de/LC_MESSAGES/django.po index 6b80207..bcedefd 100644 --- a/gnuviechadmin/gvawebcore/locale/de/LC_MESSAGES/django.po +++ b/gnuviechadmin/gvawebcore/locale/de/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: gvawebcore\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-01-29 11:04+0100\n" -"PO-Revision-Date: 2015-01-25 11:49+0100\n" +"POT-Creation-Date: 2023-04-16 22:07+0200\n" +"PO-Revision-Date: 2023-04-16 18:21+0200\n" "Last-Translator: Jan Dittberner \n" "Language-Team: Jan Dittberner \n" "Language: de\n" @@ -16,17 +16,17 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.6.10\n" +"X-Generator: Poedit 3.2.2\n" "X-Poedit-SourceCharset: UTF-8\n" -#: gvawebcore/forms.py:12 +#: gvawebcore/forms.py:11 msgid "Passwords don't match" msgstr "Passwörter stimmen nicht überein" #: gvawebcore/forms.py:25 msgid "Password" -msgstr "Passwort: " +msgstr "Passwort" -#: gvawebcore/forms.py:28 +#: gvawebcore/forms.py:29 msgid "Password (again)" msgstr "Passwortwiederholung" diff --git a/gnuviechadmin/gvawebcore/views.py b/gnuviechadmin/gvawebcore/views.py index f6079bb..d62f697 100644 --- a/gnuviechadmin/gvawebcore/views.py +++ b/gnuviechadmin/gvawebcore/views.py @@ -2,9 +2,10 @@ This module defines common view code to be used by multiple gnuviechadmin apps. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import from django.shortcuts import get_object_or_404 + from hostingpackages.models import CustomerHostingPackage @@ -14,7 +15,8 @@ class HostingPackageAndCustomerMixin(object): keyword argument 'package'. """ - hosting_package_kwarg = 'package' + + hosting_package_kwarg = "package" """Keyword argument used to find the hosting package in the URL.""" hostingpackage = None @@ -22,8 +24,8 @@ class HostingPackageAndCustomerMixin(object): def get_hosting_package(self): if self.hostingpackage is None: self.hostingpackage = get_object_or_404( - CustomerHostingPackage, - pk=int(self.kwargs[self.hosting_package_kwarg])) + CustomerHostingPackage, pk=int(self.kwargs[self.hosting_package_kwarg]) + ) return self.hostingpackage def get_customer_object(self): diff --git a/docker/django_media/.empty b/gnuviechadmin/help/__init__.py similarity index 100% rename from docker/django_media/.empty rename to gnuviechadmin/help/__init__.py diff --git a/gnuviechadmin/help/admin.py b/gnuviechadmin/help/admin.py new file mode 100644 index 0000000..b143c6c --- /dev/null +++ b/gnuviechadmin/help/admin.py @@ -0,0 +1,22 @@ +from django.contrib import admin +from django.contrib.auth import get_user_model +from django.contrib.auth.admin import UserAdmin as BaseUserAdmin +from django.utils.translation import ugettext_lazy as _ + +from help.models import HelpUser + +User = get_user_model() + + +class HelpUserInline(admin.StackedInline): + model = HelpUser + can_delete = False + readonly_fields = ("offline_account_code",) + + +class UserAdmin(BaseUserAdmin): + inlines = (HelpUserInline,) + + +admin.site.unregister(User) +admin.site.register(User, UserAdmin) diff --git a/gnuviechadmin/help/apps.py b/gnuviechadmin/help/apps.py new file mode 100644 index 0000000..870ccf9 --- /dev/null +++ b/gnuviechadmin/help/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ + + +class HelpConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "help" + verbose_name = _("User self help") diff --git a/gnuviechadmin/help/locale/de/LC_MESSAGES/django.po b/gnuviechadmin/help/locale/de/LC_MESSAGES/django.po new file mode 100644 index 0000000..9014e63 --- /dev/null +++ b/gnuviechadmin/help/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,36 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , 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 \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" diff --git a/docker/django_static/.empty b/gnuviechadmin/help/management/__init__.py similarity index 100% rename from docker/django_static/.empty rename to gnuviechadmin/help/management/__init__.py diff --git a/gnuviechadmin/help/management/commands/__init__.py b/gnuviechadmin/help/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gnuviechadmin/help/management/commands/populate.py b/gnuviechadmin/help/management/commands/populate.py new file mode 100644 index 0000000..77ee7c3 --- /dev/null +++ b/gnuviechadmin/help/management/commands/populate.py @@ -0,0 +1,17 @@ +from django.contrib.auth import get_user_model +from django.core.management import BaseCommand + +from help.models import HelpUser + +User = get_user_model() + + +class Command(BaseCommand): + help = "Populate help user information for existing users" + + def handle(self, *args, **options): + for user in User.objects.filter(helpuser=None): + help_user = HelpUser.objects.create(user_id=user.id, email_address=user.email) + help_user.generate_offline_account_code() + help_user.save() + self.stdout.write(f"created offline account code {help_user.offline_account_code} for {user}.") diff --git a/gnuviechadmin/help/management/commands/reset_offline_code.py b/gnuviechadmin/help/management/commands/reset_offline_code.py new file mode 100644 index 0000000..a93579d --- /dev/null +++ b/gnuviechadmin/help/management/commands/reset_offline_code.py @@ -0,0 +1,29 @@ +from django.contrib.auth import get_user_model +from django.core.management import BaseCommand, CommandError + +from help.models import HelpUser + +User = get_user_model() + + +class Command(BaseCommand): + help = "Reset offline account reset code for existing users" + + def add_arguments(self, parser): + parser.add_argument("users", nargs='+', type=str) + + def handle(self, *args, **options): + for name in options["users"]: + try: + user = User.objects.get(username=name) + except User.DoesNotExist: + raise CommandError(f'User {name} does not exist') + + help_user = user.helpuser + if help_user is None: + help_user = HelpUser.objects.create(email_address=user.email) + + help_user.generate_offline_account_code() + help_user.save() + + self.stdout.write(f"generated new offline account reset code {help_user.offline_account_code} for {name}") diff --git a/gnuviechadmin/help/migrations/0001_add_help_user_model_extension.py b/gnuviechadmin/help/migrations/0001_add_help_user_model_extension.py new file mode 100644 index 0000000..fb20210 --- /dev/null +++ b/gnuviechadmin/help/migrations/0001_add_help_user_model_extension.py @@ -0,0 +1,55 @@ +# Generated by Django 3.2.18 on 2023-04-16 09:32 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="HelpUser", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "email_address", + models.EmailField( + help_text="Contact email address", max_length=254 + ), + ), + ( + "postal_address", + models.TextField(blank=True, help_text="Contact postal address"), + ), + ( + "offline_account_code", + models.CharField( + default="", + help_text="Offline account reset code", + max_length=36, + ), + ), + ( + "user", + models.OneToOneField( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + ), + ] diff --git a/gnuviechadmin/help/migrations/__init__.py b/gnuviechadmin/help/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gnuviechadmin/help/models.py b/gnuviechadmin/help/models.py new file mode 100644 index 0000000..5debb7e --- /dev/null +++ b/gnuviechadmin/help/models.py @@ -0,0 +1,17 @@ +import uuid + +from django.conf import settings +from django.db import models +from django.utils.translation import ugettext_lazy as _ + + +class HelpUser(models.Model): + user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + email_address = models.EmailField(help_text=_("Contact email address")) + postal_address = models.TextField(help_text=_("Contact postal address"), blank=True) + offline_account_code = models.CharField( + help_text=_("Offline account reset code"), max_length=36, default="" + ) + + def generate_offline_account_code(self): + self.offline_account_code = str(uuid.uuid4()) diff --git a/gnuviechadmin/help/serializers.py b/gnuviechadmin/help/serializers.py new file mode 100644 index 0000000..f4beec2 --- /dev/null +++ b/gnuviechadmin/help/serializers.py @@ -0,0 +1,22 @@ +""" +Serializers for the REST API +""" + +from rest_framework import serializers + +from help.models import HelpUser + + +class HelpUserSerializer(serializers.HyperlinkedModelSerializer): + user = serializers.StringRelatedField() + + class Meta: + model = HelpUser + fields = [ + "url", + "user", + "email_address", + "postal_address", + "offline_account_code", + ] + read_only_fields = ["user", "offline_account_code"] diff --git a/gnuviechadmin/help/tests.py b/gnuviechadmin/help/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/gnuviechadmin/help/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/gnuviechadmin/help/views.py b/gnuviechadmin/help/views.py new file mode 100644 index 0000000..2c27870 --- /dev/null +++ b/gnuviechadmin/help/views.py @@ -0,0 +1,26 @@ +from rest_framework import generics + +from help.models import HelpUser +from help.serializers import HelpUserSerializer + + +class ListHelpUserAPIView(generics.ListAPIView): + """ + API endpoint that allows user help profile to be viewed or edited. + + """ + + queryset = ( + HelpUser.objects.all().prefetch_related("user").order_by("user__username") + ) + serializer_class = HelpUserSerializer + + +class HelpUserAPIView(generics.RetrieveUpdateAPIView): + """ + API endpoint that allows user help profile to be viewed or edited. + + """ + + queryset = HelpUser.objects.all() + serializer_class = HelpUserSerializer diff --git a/gnuviechadmin/hostingpackages/__init__.py b/gnuviechadmin/hostingpackages/__init__.py index e32bbf1..18a34db 100644 --- a/gnuviechadmin/hostingpackages/__init__.py +++ b/gnuviechadmin/hostingpackages/__init__.py @@ -2,4 +2,3 @@ This app takes care of hosting packages. """ -default_app_config = 'hostingpackages.apps.HostingPackagesAppConfig' diff --git a/gnuviechadmin/hostingpackages/admin.py b/gnuviechadmin/hostingpackages/admin.py index 4d11867..ae22212 100644 --- a/gnuviechadmin/hostingpackages/admin.py +++ b/gnuviechadmin/hostingpackages/admin.py @@ -2,7 +2,7 @@ This module contains the admin site interface for hosting packages. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import from django import forms from django.contrib import admin @@ -25,9 +25,10 @@ class CustomerHostingPackageCreateForm(forms.ModelForm): This is the form class for creating new customer hosting packages. """ + class Meta: model = CustomerHostingPackage - fields = ['customer', 'template', 'name'] + fields = ["customer", "template", "name"] def save(self, **kwargs): """ @@ -39,10 +40,11 @@ class CustomerHostingPackageCreateForm(forms.ModelForm): """ hostinpackages = CustomerHostingPackage.objects.create_from_template( - customer=self.cleaned_data['customer'], - template=self.cleaned_data['template'], - name=self.cleaned_data['name'], - **kwargs) + customer=self.cleaned_data["customer"], + template=self.cleaned_data["template"], + name=self.cleaned_data["name"], + **kwargs + ) return hostinpackages def save_m2m(self): @@ -55,6 +57,7 @@ class CustomerDiskSpaceOptionInline(admin.TabularInline): space options. """ + model = CustomerDiskSpaceOption extra = 0 @@ -65,6 +68,7 @@ class CustomerMailboxOptionInline(admin.TabularInline): mailbox options. """ + model = CustomerMailboxOption extra = 0 @@ -75,6 +79,7 @@ class CustomerUserDatabaseOptionInline(admin.TabularInline): database options. """ + model = CustomerUserDatabaseOption extra = 0 @@ -85,6 +90,7 @@ class CustomerHostingPackageDomainInline(admin.TabularInline): hosting packages. """ + model = CustomerHostingPackageDomain extra = 0 @@ -95,12 +101,9 @@ class CustomerHostingPackageAdmin(admin.ModelAdmin): :py:class:`CustomerHostingPackage`. """ + add_form = CustomerHostingPackageCreateForm - add_fieldsets = ( - (None, { - 'fields': ('customer', 'template', 'name') - }), - ) + add_fieldsets = ((None, {"fields": ("customer", "template", "name")}),) inlines = [ CustomerDiskSpaceOptionInline, @@ -108,7 +111,7 @@ class CustomerHostingPackageAdmin(admin.ModelAdmin): CustomerUserDatabaseOptionInline, CustomerHostingPackageDomainInline, ] - list_display = ['name', 'customer', 'osuser'] + list_display = ["name", "customer", "osuser"] def get_form(self, request, obj=None, **kwargs): """ @@ -125,13 +128,16 @@ class CustomerHostingPackageAdmin(admin.ModelAdmin): """ defaults = {} if obj is None: - defaults.update({ - 'form': self.add_form, - 'fields': admin.options.flatten_fieldsets(self.add_fieldsets), - }) + defaults.update( + { + "form": self.add_form, + "fields": admin.options.flatten_fieldsets(self.add_fieldsets), + } + ) defaults.update(kwargs) return super(CustomerHostingPackageAdmin, self).get_form( - request, obj, **defaults) + request, obj, **defaults + ) def get_readonly_fields(self, request, obj=None): """ @@ -147,7 +153,7 @@ class CustomerHostingPackageAdmin(admin.ModelAdmin): """ if obj: - return ['customer', 'template'] + return ["customer", "template"] return [] diff --git a/gnuviechadmin/hostingpackages/apps.py b/gnuviechadmin/hostingpackages/apps.py index e1fb021..aee9773 100644 --- a/gnuviechadmin/hostingpackages/apps.py +++ b/gnuviechadmin/hostingpackages/apps.py @@ -3,9 +3,8 @@ This module contains the :py:class:`django.apps.AppConfig` instance for the :py:mod:`hostingpackages` app. """ -from __future__ import unicode_literals from django.apps import AppConfig -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ class HostingPackagesAppConfig(AppConfig): @@ -13,5 +12,6 @@ class HostingPackagesAppConfig(AppConfig): AppConfig for the :py:mod:`hostingpackages` app. """ - name = 'hostingpackages' - verbose_name = _('Hosting Packages and Options') + + name = "hostingpackages" + verbose_name = _("Hosting Packages and Options") diff --git a/gnuviechadmin/hostingpackages/forms.py b/gnuviechadmin/hostingpackages/forms.py index ddf5f6b..16e7baf 100644 --- a/gnuviechadmin/hostingpackages/forms.py +++ b/gnuviechadmin/hostingpackages/forms.py @@ -2,17 +2,13 @@ This module contains the form classes related to hosting packages. """ -from __future__ import absolute_import, unicode_literals - -from django import forms -from django.urls import reverse -from django.utils.translation import ugettext as _ +from __future__ import absolute_import from crispy_forms.helper import FormHelper -from crispy_forms.layout import ( - Layout, - Submit, -) +from crispy_forms.layout import Layout, Submit +from django import forms +from django.urls import reverse +from django.utils.translation import gettext as _ from .models import ( CustomerDiskSpaceOption, @@ -28,25 +24,24 @@ class CreateCustomerHostingPackageForm(forms.ModelForm): a preselected customer. """ + class Meta: model = CustomerHostingPackage - fields = ['template', 'name', 'description'] + fields = ["template", "name", "description"] def __init__(self, instance, *args, **kwargs): - username = kwargs.pop('user') - super(CreateCustomerHostingPackageForm, self).__init__( - *args, **kwargs - ) - self.fields['description'].widget.attrs['rows'] = 2 + username = kwargs.pop("user") + super(CreateCustomerHostingPackageForm, self).__init__(*args, **kwargs) + self.fields["description"].widget.attrs["rows"] = 2 self.helper = FormHelper() self.helper.form_action = reverse( - 'create_customer_hosting_package', kwargs={'user': username} + "create_customer_hosting_package", kwargs={"user": username} ) self.helper.layout = Layout( - 'template', - 'name', - 'description', - Submit('submit', _('Add Hosting Package')), + "template", + "name", + "description", + Submit("submit", _("Add Hosting Package")), ) @@ -55,44 +50,44 @@ class CreateHostingPackageForm(forms.ModelForm): This form class is used for creating new customer hosting packages. """ + class Meta: model = CustomerHostingPackage - fields = ['customer', 'template', 'name', 'description'] + fields = ["customer", "template", "name", "description"] def __init__(self, instance, *args, **kwargs): - super(CreateHostingPackageForm, self).__init__( - *args, **kwargs - ) - self.fields['description'].widget.attrs['rows'] = 2 + super(CreateHostingPackageForm, self).__init__(*args, **kwargs) + self.fields["description"].widget.attrs["rows"] = 2 self.helper = FormHelper() - self.helper.form_action = reverse('create_hosting_package') + self.helper.form_action = reverse("create_hosting_package") self.helper.layout = Layout( - 'customer', - 'template', - 'name', - 'description', - Submit('submit', _('Add Hosting Package')), + "customer", + "template", + "name", + "description", + Submit("submit", _("Add Hosting Package")), ) class AddDiskspaceOptionForm(forms.ModelForm): class Meta: model = CustomerDiskSpaceOption - fields = ['diskspace', 'diskspace_unit'] + fields = ["diskspace", "diskspace_unit"] def __init__(self, *args, **kwargs): - self.hostingpackage = kwargs.pop('hostingpackage') - self.option_template = kwargs.pop('option_template') + self.hostingpackage = kwargs.pop("hostingpackage") + self.option_template = kwargs.pop("option_template") super(AddDiskspaceOptionForm, self).__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_action = reverse( - 'add_hosting_option', + "add_hosting_option", kwargs={ - 'package': self.hostingpackage.id, - 'type': 'diskspace', - 'optionid': self.option_template.id, - }) - self.helper.add_input(Submit('submit', _('Add disk space option'))) + "package": self.hostingpackage.id, + "type": "diskspace", + "optionid": self.option_template.id, + }, + ) + self.helper.add_input(Submit("submit", _("Add disk space option"))) def save(self, commit=True): self.instance.hosting_package = self.hostingpackage @@ -103,21 +98,22 @@ class AddDiskspaceOptionForm(forms.ModelForm): class AddMailboxOptionForm(forms.ModelForm): class Meta: model = CustomerMailboxOption - fields = ['number'] + fields = ["number"] def __init__(self, *args, **kwargs): - self.hostingpackage = kwargs.pop('hostingpackage') - self.option_template = kwargs.pop('option_template') + self.hostingpackage = kwargs.pop("hostingpackage") + self.option_template = kwargs.pop("option_template") super(AddMailboxOptionForm, self).__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_action = reverse( - 'add_hosting_option', + "add_hosting_option", kwargs={ - 'package': self.hostingpackage.id, - 'type': 'mailboxes', - 'optionid': self.option_template.id, - }) - self.helper.add_input(Submit('submit', _('Add mailbox option'))) + "package": self.hostingpackage.id, + "type": "mailboxes", + "optionid": self.option_template.id, + }, + ) + self.helper.add_input(Submit("submit", _("Add mailbox option"))) def save(self, commit=True): self.instance.hosting_package = self.hostingpackage @@ -128,21 +124,22 @@ class AddMailboxOptionForm(forms.ModelForm): class AddUserDatabaseOptionForm(forms.ModelForm): class Meta: model = CustomerUserDatabaseOption - fields = ['number'] + fields = ["number"] def __init__(self, *args, **kwargs): - self.hostingpackage = kwargs.pop('hostingpackage') - self.option_template = kwargs.pop('option_template') + self.hostingpackage = kwargs.pop("hostingpackage") + self.option_template = kwargs.pop("option_template") super(AddUserDatabaseOptionForm, self).__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_action = reverse( - 'add_hosting_option', + "add_hosting_option", kwargs={ - 'package': self.hostingpackage.id, - 'type': 'databases', - 'optionid': self.option_template.id, - }) - self.helper.add_input(Submit('submit', _('Add database option'))) + "package": self.hostingpackage.id, + "type": "databases", + "optionid": self.option_template.id, + }, + ) + self.helper.add_input(Submit("submit", _("Add database option"))) def save(self, commit=True): self.instance.hosting_package = self.hostingpackage diff --git a/gnuviechadmin/hostingpackages/locale/de/LC_MESSAGES/django.po b/gnuviechadmin/hostingpackages/locale/de/LC_MESSAGES/django.po index d8edc2e..dd5bfb2 100644 --- a/gnuviechadmin/hostingpackages/locale/de/LC_MESSAGES/django.po +++ b/gnuviechadmin/hostingpackages/locale/de/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: gnuviechadmin hostingpackages\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-01-29 11:04+0100\n" -"PO-Revision-Date: 2015-01-25 15:49+0100\n" +"POT-Creation-Date: 2023-04-22 13:14+0200\n" +"PO-Revision-Date: 2023-04-22 13:15+0200\n" "Last-Translator: Jan Dittberner \n" "Language-Team: Jan Dittberner \n" "Language: de\n" @@ -16,221 +16,561 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.6.10\n" +"X-Generator: Poedit 3.2.2\n" "X-Poedit-SourceCharset: UTF-8\n" #: hostingpackages/apps.py:17 msgid "Hosting Packages and Options" msgstr "Hostingpakete und -Optionen" -#: hostingpackages/forms.py:49 hostingpackages/forms.py:74 +#: hostingpackages/forms.py:44 hostingpackages/forms.py:68 msgid "Add Hosting Package" msgstr "Hostingpaket anlegen" -#: hostingpackages/forms.py:95 +#: hostingpackages/forms.py:90 msgid "Add disk space option" msgstr "Speicherplatzoption hinzufügen" -#: hostingpackages/forms.py:120 +#: hostingpackages/forms.py:116 msgid "Add mailbox option" msgstr "Postfachoption hinzufügen" -#: hostingpackages/forms.py:145 +#: hostingpackages/forms.py:142 msgid "Add database option" msgstr "Datenbankoption hinzufügen" -#: hostingpackages/models.py:31 +#: hostingpackages/models.py:21 msgid "MiB" msgstr "MiB" -#: hostingpackages/models.py:32 +#: hostingpackages/models.py:21 msgid "GiB" msgstr "GiB" -#: hostingpackages/models.py:33 +#: hostingpackages/models.py:21 msgid "TiB" msgstr "TiB" -#: hostingpackages/models.py:45 +#: hostingpackages/models.py:27 msgid "description" msgstr "Beschreibung" -#: hostingpackages/models.py:46 +#: hostingpackages/models.py:28 msgid "mailbox count" msgstr "Anzahl Postfächer" -#: hostingpackages/models.py:48 hostingpackages/models.py:76 +#: hostingpackages/models.py:30 hostingpackages/models.py:59 msgid "disk space" msgstr "Speicherplatz" -#: hostingpackages/models.py:48 +#: hostingpackages/models.py:30 msgid "disk space for the hosting package" msgstr "Speicherplatz für das Hostingpaket" -#: hostingpackages/models.py:50 hostingpackages/models.py:78 +#: hostingpackages/models.py:33 hostingpackages/models.py:61 msgid "unit of disk space" msgstr "Maßeinheit für den Speicherplatz" -#: hostingpackages/models.py:60 hostingpackages/models.py:213 +#: hostingpackages/models.py:44 hostingpackages/models.py:192 msgid "name" msgstr "Name" -#: hostingpackages/models.py:63 +#: hostingpackages/models.py:47 msgid "Hosting package" msgstr "Hostingpaket" -#: hostingpackages/models.py:64 +#: hostingpackages/models.py:48 msgid "Hosting packages" msgstr "Hostingpakete" -#: hostingpackages/models.py:83 +#: hostingpackages/models.py:67 msgid "Disk space option" msgstr "Speicherplatzoption" -#: hostingpackages/models.py:84 +#: hostingpackages/models.py:68 msgid "Disk space options" msgstr "Speicherplatzoptionen" -#: hostingpackages/models.py:87 +#: hostingpackages/models.py:71 #, python-brace-format msgid "Additional disk space {space} {unit}" msgstr "Zusätzlicher Speicherplatz {space} {unit}" -#: hostingpackages/models.py:104 +#: hostingpackages/models.py:88 msgid "number of databases" msgstr "Anzahl von Datenbanken" -#: hostingpackages/models.py:106 +#: hostingpackages/models.py:89 msgid "database type" msgstr "Datenbanktyp" -#: hostingpackages/models.py:111 +#: hostingpackages/models.py:94 msgid "Database option" msgstr "Datenbankoption" -#: hostingpackages/models.py:112 +#: hostingpackages/models.py:95 msgid "Database options" msgstr "Datenbankoptionen" -#: hostingpackages/models.py:116 +#: hostingpackages/models.py:99 #, python-brace-format msgid "{type} database" msgid_plural "{count} {type} databases" msgstr[0] "{type}-Datenbank" msgstr[1] "{count} {type}-Datenbanken" -#: hostingpackages/models.py:141 +#: hostingpackages/models.py:120 msgid "number of mailboxes" msgstr "Anzahl von Postfächern" -#: hostingpackages/models.py:146 +#: hostingpackages/models.py:125 msgid "Mailbox option" msgstr "Postfachoption" -#: hostingpackages/models.py:147 +#: hostingpackages/models.py:126 msgid "Mailbox options" msgstr "Postfachoptionen" -#: hostingpackages/models.py:151 +#: hostingpackages/models.py:130 #, python-brace-format msgid "{count} additional mailbox" msgid_plural "{count} additional mailboxes" msgstr[0] "{count} zusätzliches Postfach" msgstr[1] "{count} zusätzliche Postfächer" -#: hostingpackages/models.py:206 +#: hostingpackages/models.py:182 msgid "customer" msgstr "Kunde" -#: hostingpackages/models.py:208 +#: hostingpackages/models.py:186 msgid "hosting package template" msgstr "Hostingpaketvorlage" -#: hostingpackages/models.py:210 +#: hostingpackages/models.py:188 msgid "The hosting package template that this hosting package is based on" msgstr "Die Hostingpaketvorlage, auf der dieses Hostingpaket aufgebaut ist" -#: hostingpackages/models.py:215 +#: hostingpackages/models.py:195 msgid "Operating system user" msgstr "Betriebssystemnutzer" -#: hostingpackages/models.py:222 +#: hostingpackages/models.py:205 msgid "customer hosting package" msgstr "Kundenhostingpaket" -#: hostingpackages/models.py:223 +#: hostingpackages/models.py:206 msgid "customer hosting packages" msgstr "Kundenhostingpakete" -#: hostingpackages/models.py:226 +#: hostingpackages/models.py:209 #, python-brace-format msgid "{name} for {customer}" msgstr "{name} für {customer}" -#: hostingpackages/models.py:404 hostingpackages/models.py:426 +#: hostingpackages/models.py:388 hostingpackages/models.py:415 msgid "hosting package" msgstr "Hostingpaket" -#: hostingpackages/models.py:407 +#: hostingpackages/models.py:393 msgid "hosting domain" msgstr "Hostingdomain" -#: hostingpackages/models.py:429 +#: hostingpackages/models.py:420 msgid "customer hosting option" msgstr "kundenspezifische Hostingoption" -#: hostingpackages/models.py:430 +#: hostingpackages/models.py:421 msgid "customer hosting options" msgstr "kundenspezifische Hostingoptionen" -#: hostingpackages/models.py:442 +#: hostingpackages/models.py:433 msgid "disk space option template" msgstr "Speicherplatzoptionsvorlage" -#: hostingpackages/models.py:444 +#: hostingpackages/models.py:435 msgid "The disk space option template that this disk space option is based on" msgstr "" "Die Speicherplatzoptionsvorlage auf der diese Speicherplatzoption aufgebaut " "ist" -#: hostingpackages/models.py:458 +#: hostingpackages/models.py:450 msgid "user database option template" msgstr "Nutzerdatenbankoptionsvorlage" -#: hostingpackages/models.py:460 +#: hostingpackages/models.py:452 msgid "The user database option template that this database option is based on" msgstr "" "Die Nutzerdatenbankoptionsvorlage auf der diese Datenbankoption aufgebaut ist" -#: hostingpackages/models.py:474 +#: hostingpackages/models.py:467 msgid "mailbox option template" msgstr "Postfachoptionsvorlage" -#: hostingpackages/models.py:476 +#: hostingpackages/models.py:468 msgid "The mailbox option template that this mailbox option is based on" msgstr "Die Postfachoptionsvorlage auf der diese Postfachoption aufgebaut ist" -#: hostingpackages/views.py:60 hostingpackages/views.py:94 +#: hostingpackages/templates/hostingpackages/add_hosting_option.html:4 +#: hostingpackages/templates/hostingpackages/add_hosting_option.html:7 +#, python-format +msgid "Add Option to Hosting Package %(package)s of Customer %(full_name)s" +msgstr "" +"Option zum Hostingpaket %(package)s des Kunden %(full_name)s hinzufügen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:3 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:4 +msgid "All hosting packages" +msgstr "Alle Hostingpakete" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:11 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:36 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:26 +msgid "Name" +msgstr "Name" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:12 +msgid "Customer" +msgstr "Kunde" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:13 +msgid "OS User" +msgstr "OS-Nutzer" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:14 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:27 +msgid "Setup date" +msgstr "Einrichtungsdatum" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:31 +msgid "No hosting packages have been setup yet." +msgstr "Es wurden noch keine Hostingpakete eingerichtet." + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html:34 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:46 +msgid "Add hosting package" +msgstr "Hostingpaket anlegen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_create.html:3 +#, python-format +msgid "Add hosting package for Customer %(full_name)s" +msgstr "Hostingpaket für Kunde %(full_name)s hinzufügen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_create.html:6 +#, python-format +msgid "Add Hosting Package for Customer %(full_name)s" +msgstr "Hosting Paket für Kunde %(full_name)s" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:6 +#, python-format +msgid "Details for your Hosting Package %(package)s" +msgstr "Details zu Ihrem Hostingpaket %(package)s" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:10 +#, python-format +msgid "Details for Hosting Package %(package)s of %(full_name)s" +msgstr "Details zum Hostingpaket %(package)s von %(full_name)s" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:16 +#, python-format +msgid "Details of Hosting Package %(package)s" +msgstr "Details zum Hostingpaket %(package)s" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:25 +msgid "Hosting Package Information" +msgstr "Informationen zum Hostingpaket" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:29 +msgid "Edit Hosting Package Information" +msgstr "Informationen zum Hostingpaket ändern" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:38 +msgid "Description" +msgstr "Beschreibung" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:40 +#: hostingpackages/views.py:199 +msgid "Disk space" +msgstr "Speicherplatz" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:43 +#, python-format +msgid "The reserved disk space for your hosting package is %(diskspace)s bytes" +msgstr "" +"Der für Ihr Hostingpaket reservierte Speicherplatz beträgt %(diskspace)s " +"Bytes" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:47 +#, python-format +msgid "" +"The package contributes %(humanbytes)s (%(packagespace)s bytes) the " +"difference comes from disk space options" +msgstr "" +"Das Paket trägt %(humanbytes)s (%(packagespace)s Bytes) zur Gesamtgröße bei, " +"der Unterschied ergibt sich aus Speicherplatzoptionen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:52 +#: hostingpackages/views.py:206 +msgid "Mailboxes" +msgstr "Postfächer" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:54 +#, python-format +msgid "%(num)s of %(total)s in use" +msgstr "%(num)s von %(total)s genutzt" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:57 +#, python-format +msgid "" +"The package provides %(mailboxcount)s mailboxes the difference comes from " +"mailbox options." +msgstr "" +"Das Paket bietet %(mailboxcount)s Postfächer, der Unterschied ergibt sich " +"durch die Postfachoptionen." + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:59 +msgid "SFTP username" +msgstr "SFTP-Benutzername" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:60 +msgid "SSH/SFTP username" +msgstr "SSH/SFTP-Benutzername" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:63 +#, python-format +msgid "There is an SSH public key set for this user." +msgid_plural "There are %(counter)s SSH public keys set for this user." +msgstr[0] "Es wurde ein SSH-Schlüssel für diesen Nutzer hinterlegt." +msgstr[1] "Es wurden %(counter)s SSH-Schlüssel für diesen Nutzer hinterlegt." + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:65 +msgid "Upload server" +msgstr "Uploadserver" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:73 +msgid "Hosting Package Options" +msgstr "Hostingpaketoptionen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:81 +msgid "No options booked" +msgstr "Keine Optionen gebucht" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:87 +msgid "Add another hosting option" +msgstr "Eine weitere Hostingoption hinzufügen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:87 +msgid "Add option" +msgstr "Option hinzufügen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:94 +msgid "Hosting Package Actions" +msgstr "Aktionen zum Hostingpaket" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:98 +msgid "Edit Hosting Package Description" +msgstr "Beschreibung des Hostingpakets bearbeiten" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:98 +msgid "Edit description" +msgstr "Beschreibung bearbeiten" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:101 +msgid "Set SFTP password" +msgstr "SFTP-Passwort setzen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:102 +msgid "Set SSH/SFTP password" +msgstr "SSH/SFTP-Passwort setzen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:105 +msgid "Add an SSH public key that can be used as an alternative for password" +msgstr "" +"Einen SSH-Schlüssel, der als Alternative zum Passwort genutzt werden kann, " +"hinzufügen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:107 +msgid "Add SSH public key" +msgstr "SSH-Schlüssel hinzufügen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:116 +msgid "Domains" +msgstr "Domains" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:121 +msgid "Domain name" +msgstr "Domainname" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:122 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:201 +msgid "Mail addresses" +msgstr "E-Mailadressen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:123 +msgid "Websites" +msgstr "Webauftritte" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:124 +msgid "Domain actions" +msgstr "Domainaktionen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:125 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:204 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:247 +msgid "Actions" +msgstr "Aktionen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:137 +msgid "Edit mail address targets" +msgstr "E-Mailadressziele bearbeiten" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:139 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:141 +msgid "Delete mail address" +msgstr "E-Mailadresse löschen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:146 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:161 +msgid "None" +msgstr "Keine" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:154 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:156 +msgid "Delete website" +msgstr "Webauftritt löschen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:167 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:169 +msgid "Add mail address" +msgstr "E-Mailadresse hinzufügen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:174 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:175 +msgid "Add website" +msgstr "Webauftritt anlegen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:183 +msgid "There are no domains assigned to this hosting package yet." +msgstr "Diesem Paket sind noch keine Domains zugeordnet." + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:187 +msgid "Add domain" +msgstr "Domain hinzufügen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:195 +msgid "E-Mail-Accounts" +msgstr "E-Mail-Konten" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:200 +msgid "Mailbox" +msgstr "Postfach" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:202 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:214 +msgid "Active" +msgstr "Aktiv" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:203 +msgid "Mailbox actions" +msgstr "Postfachaktionen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:215 +msgid "inactive" +msgstr "inaktiv" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:218 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:219 +msgid "Set mailbox password" +msgstr "Postfachpasswort setzen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:225 +msgid "There are no mailboxes assigned to this hosting package yet." +msgstr "Diesem Hostingpaket sind noch keine Postfächer zugeordnet." + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:230 +msgid "Add mailbox" +msgstr "Postfach hinzufügen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:237 +#: hostingpackages/views.py:213 +msgid "Databases" +msgstr "Datenbanken" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:242 +msgid "Database name" +msgstr "Datenbankname" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:243 +msgid "Database user" +msgstr "Datenbanknutzer" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:244 +msgid "Database type" +msgstr "Datenbanktyp" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:245 +msgid "Type" +msgstr "Typ" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:246 +msgid "Database actions" +msgstr "Datenbankaktionen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:258 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:260 +msgid "Set database user password" +msgstr "Datenbanknutzerpasswort setzen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:262 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:263 +msgid "Delete database" +msgstr "Datenbank löschen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:270 +msgid "There are no databases assigned to this hosting package yet." +msgstr "Diesem Hostingpaket sind noch keine Datenbanken zugeordnet." + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html:275 +msgid "Add database" +msgstr "Datenbank hinzufügen" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:5 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:14 +msgid "Your hosting packages" +msgstr "Ihre Hostingpakete" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:7 +#, python-format +msgid "Hosting Packages of %(customer)s" +msgstr "Hostingpakete des Kunden %(customer)s" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:16 +#, python-format +msgid "Hosting Packages of %(customer)s" +msgstr "Hostingpakete des Kunden %(customer)s" + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:41 +msgid "You have no hosting packages setup yet." +msgstr "Es wurden noch keine Hostingpakete für Sie eingerichtet." + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_list.html:42 +msgid "There are no hosting packages setup for this customer yet." +msgstr "Es wurden noch keine Hostingpakete für diesen Kunden eingerichtet." + +#: hostingpackages/templates/hostingpackages/customerhostingpackage_option_choices.html:5 +#: hostingpackages/templates/hostingpackages/customerhostingpackage_option_choices.html:8 +#, python-format +msgid "" +"Choose new Option for Hosting Package %(package)s of Customer %(full_name)s" +msgstr "" +"Wählen Sie eine neue Option für das Hostingpaket %(package)s des Kunden " +"%(full_name)s" + +#: hostingpackages/views.py:49 hostingpackages/views.py:83 #, python-brace-format msgid "Started setup of new hosting package {name}." msgstr "Einrichtung des Hostingpakets {name} wurde gestartet." -#: hostingpackages/views.py:186 -msgid "Disk space" -msgstr "Speicherplatz" - -#: hostingpackages/views.py:189 -msgid "Mailboxes" -msgstr "Postfächer" - -#: hostingpackages/views.py:192 -msgid "Databases" -msgstr "Datenbanken" - -#: hostingpackages/views.py:262 +#: hostingpackages/views.py:287 #, python-brace-format msgid "Successfully added option {option} to hosting package {package}." msgstr "Option {option} erfolgreich zum Hostingpaket {package} hinzugefügt." - -#~ msgid "Hosting options" -#~ msgstr "Hostingoptionen" diff --git a/gnuviechadmin/hostingpackages/migrations/0001_initial.py b/gnuviechadmin/hostingpackages/migrations/0001_initial.py index 51ec210..bc234dd 100644 --- a/gnuviechadmin/hostingpackages/migrations/0001_initial.py +++ b/gnuviechadmin/hostingpackages/migrations/0001_initial.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - import django.utils.timezone import model_utils.fields from django.conf import settings @@ -14,301 +12,469 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='CustomerHostingPackage', + name="CustomerHostingPackage", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, auto_created=True, - primary_key=True)), - ('created', model_utils.fields.AutoCreatedField( - default=django.utils.timezone.now, verbose_name='created', - editable=False)), - ('modified', model_utils.fields.AutoLastModifiedField( - default=django.utils.timezone.now, verbose_name='modified', - editable=False)), - ('name', models.CharField( - unique=True, max_length=128, verbose_name='name')), - ('description', models.TextField( - verbose_name='description', blank=True)), - ('mailboxcount', models.PositiveIntegerField( - verbose_name='mailbox count')), - ('diskspace', models.PositiveIntegerField( - help_text='disk space for the hosting package', - verbose_name='disk space')), - ('diskspace_unit', models.PositiveSmallIntegerField( - verbose_name='unit of disk space', - choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])), - ('customer', models.ForeignKey( - verbose_name='customer', to=settings.AUTH_USER_MODEL, - on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + verbose_name="created", + editable=False, + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + verbose_name="modified", + editable=False, + ), + ), + ( + "name", + models.CharField(unique=True, max_length=128, verbose_name="name"), + ), + ( + "description", + models.TextField(verbose_name="description", blank=True), + ), + ( + "mailboxcount", + models.PositiveIntegerField(verbose_name="mailbox count"), + ), + ( + "diskspace", + models.PositiveIntegerField( + help_text="disk space for the hosting package", + verbose_name="disk space", + ), + ), + ( + "diskspace_unit", + models.PositiveSmallIntegerField( + verbose_name="unit of disk space", + choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")], + ), + ), + ( + "customer", + models.ForeignKey( + verbose_name="customer", + to=settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + ), + ), ], options={ - 'verbose_name': 'customer hosting package', - 'verbose_name_plural': 'customer hosting packages', + "verbose_name": "customer hosting package", + "verbose_name_plural": "customer hosting packages", }, bases=(models.Model,), ), migrations.CreateModel( - name='CustomerHostingPackageOption', + name="CustomerHostingPackageOption", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, auto_created=True, - primary_key=True)), - ('created', model_utils.fields.AutoCreatedField( - default=django.utils.timezone.now, verbose_name='created', - editable=False)), - ('modified', model_utils.fields.AutoLastModifiedField( - default=django.utils.timezone.now, verbose_name='modified', - editable=False)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + verbose_name="created", + editable=False, + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + verbose_name="modified", + editable=False, + ), + ), ], options={ - 'verbose_name': 'customer hosting option', - 'verbose_name_plural': 'customer hosting options', + "verbose_name": "customer hosting option", + "verbose_name_plural": "customer hosting options", }, bases=(models.Model,), ), migrations.CreateModel( - name='CustomerDiskSpaceOption', + name="CustomerDiskSpaceOption", fields=[ - ('customerhostingpackageoption_ptr', models.OneToOneField( - parent_link=True, auto_created=True, primary_key=True, - serialize=False, - to='hostingpackages.CustomerHostingPackageOption', - on_delete=models.CASCADE)), - ('diskspace', models.PositiveIntegerField( - verbose_name='disk space')), - ('diskspace_unit', models.PositiveSmallIntegerField( - verbose_name='unit of disk space', - choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])), + ( + "customerhostingpackageoption_ptr", + models.OneToOneField( + parent_link=True, + auto_created=True, + primary_key=True, + serialize=False, + to="hostingpackages.CustomerHostingPackageOption", + on_delete=models.CASCADE, + ), + ), + ("diskspace", models.PositiveIntegerField(verbose_name="disk space")), + ( + "diskspace_unit", + models.PositiveSmallIntegerField( + verbose_name="unit of disk space", + choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")], + ), + ), ], options={ - 'ordering': ['diskspace_unit', 'diskspace'], - 'abstract': False, - 'verbose_name': 'Disk space option', - 'verbose_name_plural': 'Disk space options', + "ordering": ["diskspace_unit", "diskspace"], + "abstract": False, + "verbose_name": "Disk space option", + "verbose_name_plural": "Disk space options", }, - bases=( - 'hostingpackages.customerhostingpackageoption', models.Model), + bases=("hostingpackages.customerhostingpackageoption", models.Model), ), migrations.CreateModel( - name='CustomerMailboxOption', + name="CustomerMailboxOption", fields=[ - ('customerhostingpackageoption_ptr', models.OneToOneField( - parent_link=True, auto_created=True, primary_key=True, - serialize=False, - to='hostingpackages.CustomerHostingPackageOption', - on_delete=models.CASCADE)), - ('number', models.PositiveIntegerField( - unique=True, verbose_name='number of mailboxes')), + ( + "customerhostingpackageoption_ptr", + models.OneToOneField( + parent_link=True, + auto_created=True, + primary_key=True, + serialize=False, + to="hostingpackages.CustomerHostingPackageOption", + on_delete=models.CASCADE, + ), + ), + ( + "number", + models.PositiveIntegerField( + unique=True, verbose_name="number of mailboxes" + ), + ), ], options={ - 'ordering': ['number'], - 'abstract': False, - 'verbose_name': 'Mailbox option', - 'verbose_name_plural': 'Mailbox options', + "ordering": ["number"], + "abstract": False, + "verbose_name": "Mailbox option", + "verbose_name_plural": "Mailbox options", }, - bases=( - 'hostingpackages.customerhostingpackageoption', models.Model), + bases=("hostingpackages.customerhostingpackageoption", models.Model), ), migrations.CreateModel( - name='CustomerUserDatabaseOption', + name="CustomerUserDatabaseOption", fields=[ - ('customerhostingpackageoption_ptr', models.OneToOneField( - parent_link=True, auto_created=True, primary_key=True, - serialize=False, - to='hostingpackages.CustomerHostingPackageOption', - on_delete=models.CASCADE)), - ('number', models.PositiveIntegerField( - default=1, verbose_name='number of databases')), - ('db_type', models.PositiveSmallIntegerField( - verbose_name='database type', - choices=[(0, 'PostgreSQL'), (1, 'MySQL')])), + ( + "customerhostingpackageoption_ptr", + models.OneToOneField( + parent_link=True, + auto_created=True, + primary_key=True, + serialize=False, + to="hostingpackages.CustomerHostingPackageOption", + on_delete=models.CASCADE, + ), + ), + ( + "number", + models.PositiveIntegerField( + default=1, verbose_name="number of databases" + ), + ), + ( + "db_type", + models.PositiveSmallIntegerField( + verbose_name="database type", + choices=[(0, "PostgreSQL"), (1, "MySQL")], + ), + ), ], options={ - 'ordering': ['db_type', 'number'], - 'abstract': False, - 'verbose_name': 'Database option', - 'verbose_name_plural': 'Database options', + "ordering": ["db_type", "number"], + "abstract": False, + "verbose_name": "Database option", + "verbose_name_plural": "Database options", }, - bases=( - 'hostingpackages.customerhostingpackageoption', models.Model), + bases=("hostingpackages.customerhostingpackageoption", models.Model), ), migrations.CreateModel( - name='HostingOption', + name="HostingOption", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, auto_created=True, - primary_key=True)), - ('created', model_utils.fields.AutoCreatedField( - default=django.utils.timezone.now, verbose_name='created', - editable=False)), - ('modified', model_utils.fields.AutoLastModifiedField( - default=django.utils.timezone.now, verbose_name='modified', - editable=False)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + verbose_name="created", + editable=False, + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + verbose_name="modified", + editable=False, + ), + ), ], options={ - 'verbose_name': 'Hosting option', - 'verbose_name_plural': 'Hosting options', + "verbose_name": "Hosting option", + "verbose_name_plural": "Hosting options", }, bases=(models.Model,), ), migrations.CreateModel( - name='DiskSpaceOption', + name="DiskSpaceOption", fields=[ - ('hostingoption_ptr', models.OneToOneField( - parent_link=True, auto_created=True, primary_key=True, - serialize=False, to='hostingpackages.HostingOption', - on_delete=models.CASCADE)), - ('diskspace', models.PositiveIntegerField( - verbose_name='disk space')), - ('diskspace_unit', models.PositiveSmallIntegerField( - verbose_name='unit of disk space', - choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])), + ( + "hostingoption_ptr", + models.OneToOneField( + parent_link=True, + auto_created=True, + primary_key=True, + serialize=False, + to="hostingpackages.HostingOption", + on_delete=models.CASCADE, + ), + ), + ("diskspace", models.PositiveIntegerField(verbose_name="disk space")), + ( + "diskspace_unit", + models.PositiveSmallIntegerField( + verbose_name="unit of disk space", + choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")], + ), + ), ], options={ - 'ordering': ['diskspace_unit', 'diskspace'], - 'abstract': False, - 'verbose_name': 'Disk space option', - 'verbose_name_plural': 'Disk space options', + "ordering": ["diskspace_unit", "diskspace"], + "abstract": False, + "verbose_name": "Disk space option", + "verbose_name_plural": "Disk space options", }, - bases=('hostingpackages.hostingoption', models.Model), + bases=("hostingpackages.hostingoption", models.Model), ), migrations.CreateModel( - name='HostingPackageTemplate', + name="HostingPackageTemplate", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, auto_created=True, - primary_key=True)), - ('created', model_utils.fields.AutoCreatedField( - default=django.utils.timezone.now, verbose_name='created', - editable=False)), - ('modified', model_utils.fields.AutoLastModifiedField( - default=django.utils.timezone.now, verbose_name='modified', - editable=False)), - ('name', models.CharField( - unique=True, max_length=128, verbose_name='name')), - ('description', models.TextField( - verbose_name='description', blank=True)), - ('mailboxcount', models.PositiveIntegerField( - verbose_name='mailbox count')), - ('diskspace', models.PositiveIntegerField( - help_text='disk space for the hosting package', - verbose_name='disk space')), - ('diskspace_unit', models.PositiveSmallIntegerField( - verbose_name='unit of disk space', - choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + verbose_name="created", + editable=False, + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + verbose_name="modified", + editable=False, + ), + ), + ( + "name", + models.CharField(unique=True, max_length=128, verbose_name="name"), + ), + ( + "description", + models.TextField(verbose_name="description", blank=True), + ), + ( + "mailboxcount", + models.PositiveIntegerField(verbose_name="mailbox count"), + ), + ( + "diskspace", + models.PositiveIntegerField( + help_text="disk space for the hosting package", + verbose_name="disk space", + ), + ), + ( + "diskspace_unit", + models.PositiveSmallIntegerField( + verbose_name="unit of disk space", + choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")], + ), + ), ], options={ - 'verbose_name': 'Hosting package', - 'verbose_name_plural': 'Hosting packages', + "verbose_name": "Hosting package", + "verbose_name_plural": "Hosting packages", }, bases=(models.Model,), ), migrations.CreateModel( - name='MailboxOption', + name="MailboxOption", fields=[ - ('hostingoption_ptr', models.OneToOneField( - parent_link=True, auto_created=True, primary_key=True, - serialize=False, to='hostingpackages.HostingOption', - on_delete=models.CASCADE)), - ('number', models.PositiveIntegerField( - unique=True, verbose_name='number of mailboxes')), + ( + "hostingoption_ptr", + models.OneToOneField( + parent_link=True, + auto_created=True, + primary_key=True, + serialize=False, + to="hostingpackages.HostingOption", + on_delete=models.CASCADE, + ), + ), + ( + "number", + models.PositiveIntegerField( + unique=True, verbose_name="number of mailboxes" + ), + ), ], options={ - 'ordering': ['number'], - 'abstract': False, - 'verbose_name': 'Mailbox option', - 'verbose_name_plural': 'Mailbox options', + "ordering": ["number"], + "abstract": False, + "verbose_name": "Mailbox option", + "verbose_name_plural": "Mailbox options", }, - bases=('hostingpackages.hostingoption', models.Model), + bases=("hostingpackages.hostingoption", models.Model), ), migrations.CreateModel( - name='UserDatabaseOption', + name="UserDatabaseOption", fields=[ - ('hostingoption_ptr', models.OneToOneField( - parent_link=True, auto_created=True, primary_key=True, - serialize=False, to='hostingpackages.HostingOption', - on_delete=models.CASCADE)), - ('number', models.PositiveIntegerField( - default=1, verbose_name='number of databases')), - ('db_type', models.PositiveSmallIntegerField( - verbose_name='database type', - choices=[(0, 'PostgreSQL'), (1, 'MySQL')])), + ( + "hostingoption_ptr", + models.OneToOneField( + parent_link=True, + auto_created=True, + primary_key=True, + serialize=False, + to="hostingpackages.HostingOption", + on_delete=models.CASCADE, + ), + ), + ( + "number", + models.PositiveIntegerField( + default=1, verbose_name="number of databases" + ), + ), + ( + "db_type", + models.PositiveSmallIntegerField( + verbose_name="database type", + choices=[(0, "PostgreSQL"), (1, "MySQL")], + ), + ), ], options={ - 'ordering': ['db_type', 'number'], - 'abstract': False, - 'verbose_name': 'Database option', - 'verbose_name_plural': 'Database options', + "ordering": ["db_type", "number"], + "abstract": False, + "verbose_name": "Database option", + "verbose_name_plural": "Database options", }, - bases=('hostingpackages.hostingoption', models.Model), + bases=("hostingpackages.hostingoption", models.Model), ), migrations.AlterUniqueTogether( - name='userdatabaseoption', - unique_together={('number', 'db_type')}, + name="userdatabaseoption", + unique_together={("number", "db_type")}, ), migrations.AlterUniqueTogether( - name='diskspaceoption', - unique_together={('diskspace', 'diskspace_unit')}, + name="diskspaceoption", + unique_together={("diskspace", "diskspace_unit")}, ), migrations.AddField( - model_name='customeruserdatabaseoption', - name='template', + model_name="customeruserdatabaseoption", + name="template", field=models.ForeignKey( - verbose_name='user database option template', - to='hostingpackages.UserDatabaseOption', - help_text='The user database option template that this ' - 'hosting option is based on', - on_delete=models.CASCADE), + verbose_name="user database option template", + to="hostingpackages.UserDatabaseOption", + help_text="The user database option template that this " + "hosting option is based on", + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AlterUniqueTogether( - name='customeruserdatabaseoption', - unique_together={('number', 'db_type')}, + name="customeruserdatabaseoption", + unique_together={("number", "db_type")}, ), migrations.AddField( - model_name='customermailboxoption', - name='template', + model_name="customermailboxoption", + name="template", field=models.ForeignKey( - verbose_name='mailbox option template', - to='hostingpackages.UserDatabaseOption', - help_text='The mailbox option template that this hosting ' - 'option is based on', - on_delete=models.CASCADE), + verbose_name="mailbox option template", + to="hostingpackages.UserDatabaseOption", + help_text="The mailbox option template that this hosting " + "option is based on", + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AddField( - model_name='customerhostingpackageoption', - name='hosting_package', + model_name="customerhostingpackageoption", + name="hosting_package", field=models.ForeignKey( - verbose_name='hosting package', - to='hostingpackages.CustomerHostingPackage', - on_delete=models.CASCADE), + verbose_name="hosting package", + to="hostingpackages.CustomerHostingPackage", + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AddField( - model_name='customerhostingpackage', - name='template', + model_name="customerhostingpackage", + name="template", field=models.ForeignKey( - verbose_name='hosting package template', - to='hostingpackages.HostingPackageTemplate', - help_text='The hosting package template that this hosting ' - 'package is based on.', - on_delete=models.CASCADE), + verbose_name="hosting package template", + to="hostingpackages.HostingPackageTemplate", + help_text="The hosting package template that this hosting " + "package is based on.", + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AddField( - model_name='customerdiskspaceoption', - name='template', + model_name="customerdiskspaceoption", + name="template", field=models.ForeignKey( - verbose_name='disk space option template', - to='hostingpackages.DiskSpaceOption', - help_text='The disk space option template that this hosting ' - 'option is based on', - on_delete=models.CASCADE), + verbose_name="disk space option template", + to="hostingpackages.DiskSpaceOption", + help_text="The disk space option template that this hosting " + "option is based on", + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AlterUniqueTogether( - name='customerdiskspaceoption', - unique_together={('diskspace', 'diskspace_unit')}, + name="customerdiskspaceoption", + unique_together={("diskspace", "diskspace_unit")}, ), ] diff --git a/gnuviechadmin/hostingpackages/migrations/0001_squashed_0005_auto_20150118_1303.py b/gnuviechadmin/hostingpackages/migrations/0001_squashed_0005_auto_20150118_1303.py index 1bf5616..9a4f3a7 100644 --- a/gnuviechadmin/hostingpackages/migrations/0001_squashed_0005_auto_20150118_1303.py +++ b/gnuviechadmin/hostingpackages/migrations/0001_squashed_0005_auto_20150118_1303.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - import django.utils.timezone import model_utils.fields from django.conf import settings @@ -8,361 +6,530 @@ from django.db import migrations, models class Migration(migrations.Migration): - replaces = [('hostingpackages', '0001_initial'), - ('hostingpackages', '0002_auto_20150118_1149'), - ('hostingpackages', '0003_auto_20150118_1221'), - ('hostingpackages', '0004_customerhostingpackage_osuser'), - ('hostingpackages', '0005_auto_20150118_1303')] + replaces = [ + ("hostingpackages", "0001_initial"), + ("hostingpackages", "0002_auto_20150118_1149"), + ("hostingpackages", "0003_auto_20150118_1221"), + ("hostingpackages", "0004_customerhostingpackage_osuser"), + ("hostingpackages", "0005_auto_20150118_1303"), + ] dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('osusers', '0004_auto_20150104_1751'), + ("osusers", "0004_auto_20150104_1751"), ] operations = [ migrations.CreateModel( - name='CustomerHostingPackage', + name="CustomerHostingPackage", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, auto_created=True, - primary_key=True)), - ('created', model_utils.fields.AutoCreatedField( - default=django.utils.timezone.now, verbose_name='created', - editable=False)), - ('modified', model_utils.fields.AutoLastModifiedField( - default=django.utils.timezone.now, verbose_name='modified', - editable=False)), - ('name', models.CharField( - unique=True, max_length=128, verbose_name='name')), - ('description', models.TextField( - verbose_name='description', blank=True)), - ('mailboxcount', models.PositiveIntegerField( - verbose_name='mailbox count')), - ('diskspace', models.PositiveIntegerField( - help_text='disk space for the hosting package', - verbose_name='disk space')), - ('diskspace_unit', models.PositiveSmallIntegerField( - verbose_name='unit of disk space', - choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])), - ('customer', models.ForeignKey( - verbose_name='customer', - to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + verbose_name="created", + editable=False, + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + verbose_name="modified", + editable=False, + ), + ), + ( + "name", + models.CharField(unique=True, max_length=128, verbose_name="name"), + ), + ( + "description", + models.TextField(verbose_name="description", blank=True), + ), + ( + "mailboxcount", + models.PositiveIntegerField(verbose_name="mailbox count"), + ), + ( + "diskspace", + models.PositiveIntegerField( + help_text="disk space for the hosting package", + verbose_name="disk space", + ), + ), + ( + "diskspace_unit", + models.PositiveSmallIntegerField( + verbose_name="unit of disk space", + choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")], + ), + ), + ( + "customer", + models.ForeignKey( + verbose_name="customer", + to=settings.AUTH_USER_MODEL, + on_delete=models.CASCADE, + ), + ), ], options={ - 'verbose_name': 'customer hosting package', - 'verbose_name_plural': 'customer hosting packages', + "verbose_name": "customer hosting package", + "verbose_name_plural": "customer hosting packages", }, bases=(models.Model,), ), migrations.CreateModel( - name='CustomerHostingPackageOption', + name="CustomerHostingPackageOption", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, auto_created=True, - primary_key=True)), - ('created', model_utils.fields.AutoCreatedField( - default=django.utils.timezone.now, verbose_name='created', - editable=False)), - ('modified', model_utils.fields.AutoLastModifiedField( - default=django.utils.timezone.now, verbose_name='modified', - editable=False)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + verbose_name="created", + editable=False, + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + verbose_name="modified", + editable=False, + ), + ), ], options={ - 'verbose_name': 'customer hosting option', - 'verbose_name_plural': 'customer hosting options', + "verbose_name": "customer hosting option", + "verbose_name_plural": "customer hosting options", }, bases=(models.Model,), ), migrations.CreateModel( - name='CustomerDiskSpaceOption', + name="CustomerDiskSpaceOption", fields=[ - ('customerhostingpackageoption_ptr', - models.OneToOneField( - parent_link=True, auto_created=True, primary_key=True, - serialize=False, - to='hostingpackages.CustomerHostingPackageOption', - on_delete=models.CASCADE)), - ('diskspace', models.PositiveIntegerField( - verbose_name='disk space')), - ('diskspace_unit', models.PositiveSmallIntegerField( - verbose_name='unit of disk space', - choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])), + ( + "customerhostingpackageoption_ptr", + models.OneToOneField( + parent_link=True, + auto_created=True, + primary_key=True, + serialize=False, + to="hostingpackages.CustomerHostingPackageOption", + on_delete=models.CASCADE, + ), + ), + ("diskspace", models.PositiveIntegerField(verbose_name="disk space")), + ( + "diskspace_unit", + models.PositiveSmallIntegerField( + verbose_name="unit of disk space", + choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")], + ), + ), ], options={ - 'ordering': ['diskspace_unit', 'diskspace'], - 'abstract': False, - 'verbose_name': 'Disk space option', - 'verbose_name_plural': 'Disk space options', + "ordering": ["diskspace_unit", "diskspace"], + "abstract": False, + "verbose_name": "Disk space option", + "verbose_name_plural": "Disk space options", }, - bases=( - 'hostingpackages.customerhostingpackageoption', models.Model), + bases=("hostingpackages.customerhostingpackageoption", models.Model), ), migrations.CreateModel( - name='CustomerMailboxOption', + name="CustomerMailboxOption", fields=[ - ('customerhostingpackageoption_ptr', - models.OneToOneField( - parent_link=True, auto_created=True, primary_key=True, - serialize=False, - to='hostingpackages.CustomerHostingPackageOption', - on_delete=models.CASCADE)), - ('number', models.PositiveIntegerField( - unique=True, verbose_name='number of mailboxes')), + ( + "customerhostingpackageoption_ptr", + models.OneToOneField( + parent_link=True, + auto_created=True, + primary_key=True, + serialize=False, + to="hostingpackages.CustomerHostingPackageOption", + on_delete=models.CASCADE, + ), + ), + ( + "number", + models.PositiveIntegerField( + unique=True, verbose_name="number of mailboxes" + ), + ), ], options={ - 'ordering': ['number'], - 'abstract': False, - 'verbose_name': 'Mailbox option', - 'verbose_name_plural': 'Mailbox options', + "ordering": ["number"], + "abstract": False, + "verbose_name": "Mailbox option", + "verbose_name_plural": "Mailbox options", }, - bases=( - 'hostingpackages.customerhostingpackageoption', models.Model), + bases=("hostingpackages.customerhostingpackageoption", models.Model), ), migrations.CreateModel( - name='CustomerUserDatabaseOption', + name="CustomerUserDatabaseOption", fields=[ - ('customerhostingpackageoption_ptr', - models.OneToOneField( - parent_link=True, auto_created=True, primary_key=True, - serialize=False, - to='hostingpackages.CustomerHostingPackageOption', - on_delete=models.CASCADE)), - ('number', models.PositiveIntegerField( - default=1, verbose_name='number of databases')), - ('db_type', models.PositiveSmallIntegerField( - verbose_name='database type', - choices=[(0, 'PostgreSQL'), (1, 'MySQL')])), + ( + "customerhostingpackageoption_ptr", + models.OneToOneField( + parent_link=True, + auto_created=True, + primary_key=True, + serialize=False, + to="hostingpackages.CustomerHostingPackageOption", + on_delete=models.CASCADE, + ), + ), + ( + "number", + models.PositiveIntegerField( + default=1, verbose_name="number of databases" + ), + ), + ( + "db_type", + models.PositiveSmallIntegerField( + verbose_name="database type", + choices=[(0, "PostgreSQL"), (1, "MySQL")], + ), + ), ], options={ - 'ordering': ['db_type', 'number'], - 'abstract': False, - 'verbose_name': 'Database option', - 'verbose_name_plural': 'Database options', + "ordering": ["db_type", "number"], + "abstract": False, + "verbose_name": "Database option", + "verbose_name_plural": "Database options", }, - bases=( - 'hostingpackages.customerhostingpackageoption', models.Model), + bases=("hostingpackages.customerhostingpackageoption", models.Model), ), migrations.CreateModel( - name='HostingOption', + name="HostingOption", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, auto_created=True, - primary_key=True)), - ('created', model_utils.fields.AutoCreatedField( - default=django.utils.timezone.now, verbose_name='created', - editable=False)), - ('modified', model_utils.fields.AutoLastModifiedField( - default=django.utils.timezone.now, verbose_name='modified', - editable=False)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + verbose_name="created", + editable=False, + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + verbose_name="modified", + editable=False, + ), + ), ], options={ - 'verbose_name': 'Hosting option', - 'verbose_name_plural': 'Hosting options', + "verbose_name": "Hosting option", + "verbose_name_plural": "Hosting options", }, bases=(models.Model,), ), migrations.CreateModel( - name='DiskSpaceOption', + name="DiskSpaceOption", fields=[ - ('hostingoption_ptr', - models.OneToOneField( - parent_link=True, auto_created=True, primary_key=True, - serialize=False, to='hostingpackages.HostingOption', - on_delete=models.CASCADE)), - ('diskspace', models.PositiveIntegerField( - verbose_name='disk space')), - ('diskspace_unit', models.PositiveSmallIntegerField( - verbose_name='unit of disk space', - choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])), + ( + "hostingoption_ptr", + models.OneToOneField( + parent_link=True, + auto_created=True, + primary_key=True, + serialize=False, + to="hostingpackages.HostingOption", + on_delete=models.CASCADE, + ), + ), + ("diskspace", models.PositiveIntegerField(verbose_name="disk space")), + ( + "diskspace_unit", + models.PositiveSmallIntegerField( + verbose_name="unit of disk space", + choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")], + ), + ), ], options={ - 'ordering': ['diskspace_unit', 'diskspace'], - 'abstract': False, - 'verbose_name': 'Disk space option', - 'verbose_name_plural': 'Disk space options', + "ordering": ["diskspace_unit", "diskspace"], + "abstract": False, + "verbose_name": "Disk space option", + "verbose_name_plural": "Disk space options", }, - bases=('hostingpackages.hostingoption', models.Model), + bases=("hostingpackages.hostingoption", models.Model), ), migrations.CreateModel( - name='HostingPackageTemplate', + name="HostingPackageTemplate", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, auto_created=True, - primary_key=True)), - ('created', model_utils.fields.AutoCreatedField( - default=django.utils.timezone.now, verbose_name='created', - editable=False)), - ('modified', model_utils.fields.AutoLastModifiedField( - default=django.utils.timezone.now, verbose_name='modified', - editable=False)), - ('name', models.CharField( - unique=True, max_length=128, verbose_name='name')), - ('description', models.TextField( - verbose_name='description', blank=True)), - ('mailboxcount', models.PositiveIntegerField( - verbose_name='mailbox count')), - ('diskspace', models.PositiveIntegerField( - help_text='disk space for the hosting package', - verbose_name='disk space')), - ('diskspace_unit', models.PositiveSmallIntegerField( - verbose_name='unit of disk space', - choices=[(0, 'MiB'), (1, 'GiB'), (2, 'TiB')])), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + verbose_name="created", + editable=False, + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + verbose_name="modified", + editable=False, + ), + ), + ( + "name", + models.CharField(unique=True, max_length=128, verbose_name="name"), + ), + ( + "description", + models.TextField(verbose_name="description", blank=True), + ), + ( + "mailboxcount", + models.PositiveIntegerField(verbose_name="mailbox count"), + ), + ( + "diskspace", + models.PositiveIntegerField( + help_text="disk space for the hosting package", + verbose_name="disk space", + ), + ), + ( + "diskspace_unit", + models.PositiveSmallIntegerField( + verbose_name="unit of disk space", + choices=[(0, "MiB"), (1, "GiB"), (2, "TiB")], + ), + ), ], options={ - 'verbose_name': 'Hosting package', - 'verbose_name_plural': 'Hosting packages', + "verbose_name": "Hosting package", + "verbose_name_plural": "Hosting packages", }, bases=(models.Model,), ), migrations.CreateModel( - name='MailboxOption', + name="MailboxOption", fields=[ - ('hostingoption_ptr', - models.OneToOneField( - parent_link=True, auto_created=True, primary_key=True, - serialize=False, to='hostingpackages.HostingOption', - on_delete=models.CASCADE)), - ('number', models.PositiveIntegerField( - unique=True, verbose_name='number of mailboxes')), + ( + "hostingoption_ptr", + models.OneToOneField( + parent_link=True, + auto_created=True, + primary_key=True, + serialize=False, + to="hostingpackages.HostingOption", + on_delete=models.CASCADE, + ), + ), + ( + "number", + models.PositiveIntegerField( + unique=True, verbose_name="number of mailboxes" + ), + ), ], options={ - 'ordering': ['number'], - 'abstract': False, - 'verbose_name': 'Mailbox option', - 'verbose_name_plural': 'Mailbox options', + "ordering": ["number"], + "abstract": False, + "verbose_name": "Mailbox option", + "verbose_name_plural": "Mailbox options", }, - bases=('hostingpackages.hostingoption', models.Model), + bases=("hostingpackages.hostingoption", models.Model), ), migrations.CreateModel( - name='UserDatabaseOption', + name="UserDatabaseOption", fields=[ - ('hostingoption_ptr', - models.OneToOneField( - parent_link=True, auto_created=True, primary_key=True, - serialize=False, to='hostingpackages.HostingOption', - on_delete=models.CASCADE)), - ('number', models.PositiveIntegerField( - default=1, verbose_name='number of databases')), - ('db_type', - models.PositiveSmallIntegerField( - verbose_name='database type', - choices=[(0, 'PostgreSQL'), (1, 'MySQL')])), + ( + "hostingoption_ptr", + models.OneToOneField( + parent_link=True, + auto_created=True, + primary_key=True, + serialize=False, + to="hostingpackages.HostingOption", + on_delete=models.CASCADE, + ), + ), + ( + "number", + models.PositiveIntegerField( + default=1, verbose_name="number of databases" + ), + ), + ( + "db_type", + models.PositiveSmallIntegerField( + verbose_name="database type", + choices=[(0, "PostgreSQL"), (1, "MySQL")], + ), + ), ], options={ - 'ordering': ['db_type', 'number'], - 'abstract': False, - 'verbose_name': 'Database option', - 'verbose_name_plural': 'Database options', + "ordering": ["db_type", "number"], + "abstract": False, + "verbose_name": "Database option", + "verbose_name_plural": "Database options", }, - bases=('hostingpackages.hostingoption', models.Model), + bases=("hostingpackages.hostingoption", models.Model), ), migrations.AlterUniqueTogether( - name='userdatabaseoption', - unique_together={('number', 'db_type')}, + name="userdatabaseoption", + unique_together={("number", "db_type")}, ), migrations.AlterUniqueTogether( - name='diskspaceoption', - unique_together={('diskspace', 'diskspace_unit')}, + name="diskspaceoption", + unique_together={("diskspace", "diskspace_unit")}, ), migrations.AddField( - model_name='customeruserdatabaseoption', - name='template', + model_name="customeruserdatabaseoption", + name="template", field=models.ForeignKey( - verbose_name='user database option template', - to='hostingpackages.UserDatabaseOption', - help_text='The user database option template that this ' - 'hosting option is based on', - on_delete=models.CASCADE), + verbose_name="user database option template", + to="hostingpackages.UserDatabaseOption", + help_text="The user database option template that this " + "hosting option is based on", + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AlterUniqueTogether( - name='customeruserdatabaseoption', - unique_together={('number', 'db_type')}, + name="customeruserdatabaseoption", + unique_together={("number", "db_type")}, ), migrations.AddField( - model_name='customermailboxoption', - name='template', + model_name="customermailboxoption", + name="template", field=models.ForeignKey( - verbose_name='mailbox option template', - to='hostingpackages.UserDatabaseOption', - help_text='The mailbox option template that this mailbox ' - 'option is based on', - on_delete=models.CASCADE), + verbose_name="mailbox option template", + to="hostingpackages.UserDatabaseOption", + help_text="The mailbox option template that this mailbox " + "option is based on", + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AddField( - model_name='customerhostingpackageoption', - name='hosting_package', + model_name="customerhostingpackageoption", + name="hosting_package", field=models.ForeignKey( - verbose_name='hosting package', - to='hostingpackages.CustomerHostingPackage', - on_delete=models.CASCADE), + verbose_name="hosting package", + to="hostingpackages.CustomerHostingPackage", + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AddField( - model_name='customerhostingpackage', - name='template', + model_name="customerhostingpackage", + name="template", field=models.ForeignKey( - verbose_name='hosting package template', - to='hostingpackages.HostingPackageTemplate', - help_text='The hosting package template that this hosting ' - 'package is based on', - on_delete=models.CASCADE), + verbose_name="hosting package template", + to="hostingpackages.HostingPackageTemplate", + help_text="The hosting package template that this hosting " + "package is based on", + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AddField( - model_name='customerdiskspaceoption', - name='template', + model_name="customerdiskspaceoption", + name="template", field=models.ForeignKey( - verbose_name='disk space option template', - to='hostingpackages.DiskSpaceOption', - help_text='The disk space option template that this hosting ' - 'option is based on', - on_delete=models.CASCADE), + verbose_name="disk space option template", + to="hostingpackages.DiskSpaceOption", + help_text="The disk space option template that this hosting " + "option is based on", + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AlterUniqueTogether( - name='customerdiskspaceoption', - unique_together={('diskspace', 'diskspace_unit')}, + name="customerdiskspaceoption", + unique_together={("diskspace", "diskspace_unit")}, ), migrations.AlterField( - model_name='customerdiskspaceoption', - name='template', + model_name="customerdiskspaceoption", + name="template", field=models.ForeignKey( - verbose_name='disk space option template', - to='hostingpackages.DiskSpaceOption', - help_text='The disk space option template that this disk ' - 'space option is based on', - on_delete=models.CASCADE), + verbose_name="disk space option template", + to="hostingpackages.DiskSpaceOption", + help_text="The disk space option template that this disk " + "space option is based on", + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AlterField( - model_name='customeruserdatabaseoption', - name='template', + model_name="customeruserdatabaseoption", + name="template", field=models.ForeignKey( - verbose_name='user database option template', - to='hostingpackages.UserDatabaseOption', - help_text='The user database option template that this ' - 'database option is based on', - on_delete=models.CASCADE), + verbose_name="user database option template", + to="hostingpackages.UserDatabaseOption", + help_text="The user database option template that this " + "database option is based on", + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AlterField( - model_name='customerhostingpackage', - name='name', - field=models.CharField(max_length=128, verbose_name='name'), + model_name="customerhostingpackage", + name="name", + field=models.CharField(max_length=128, verbose_name="name"), preserve_default=True, ), migrations.AlterUniqueTogether( - name='customerhostingpackage', - unique_together={('customer', 'name')}, + name="customerhostingpackage", + unique_together={("customer", "name")}, ), migrations.AddField( - model_name='customerhostingpackage', - name='osuser', + model_name="customerhostingpackage", + name="osuser", field=models.OneToOneField( - null=True, blank=True, to='osusers.User', - verbose_name='Operating system user', on_delete=models.CASCADE), + null=True, + blank=True, + to="osusers.User", + verbose_name="Operating system user", + on_delete=models.CASCADE, + ), preserve_default=True, ), ] diff --git a/gnuviechadmin/hostingpackages/migrations/0002_auto_20150118_1149.py b/gnuviechadmin/hostingpackages/migrations/0002_auto_20150118_1149.py index 515829f..732605c 100644 --- a/gnuviechadmin/hostingpackages/migrations/0002_auto_20150118_1149.py +++ b/gnuviechadmin/hostingpackages/migrations/0002_auto_20150118_1149.py @@ -1,57 +1,59 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('hostingpackages', '0001_initial'), + ("hostingpackages", "0001_initial"), ] operations = [ migrations.AlterField( - model_name='customerdiskspaceoption', - name='template', + model_name="customerdiskspaceoption", + name="template", field=models.ForeignKey( - verbose_name='disk space option template', - to='hostingpackages.DiskSpaceOption', - help_text='The disk space option template that this disk ' - 'space option is based on', - on_delete=models.CASCADE), + verbose_name="disk space option template", + to="hostingpackages.DiskSpaceOption", + help_text="The disk space option template that this disk " + "space option is based on", + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AlterField( - model_name='customerhostingpackage', - name='template', + model_name="customerhostingpackage", + name="template", field=models.ForeignKey( - verbose_name='hosting package template', - to='hostingpackages.HostingPackageTemplate', - help_text='The hosting package template that this hosting ' - 'package is based on', - on_delete=models.CASCADE), + verbose_name="hosting package template", + to="hostingpackages.HostingPackageTemplate", + help_text="The hosting package template that this hosting " + "package is based on", + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AlterField( - model_name='customermailboxoption', - name='template', + model_name="customermailboxoption", + name="template", field=models.ForeignKey( - verbose_name='mailbox option template', - to='hostingpackages.UserDatabaseOption', - help_text='The mailbox option template that this mailbox ' - 'option is based on', - on_delete=models.CASCADE), + verbose_name="mailbox option template", + to="hostingpackages.UserDatabaseOption", + help_text="The mailbox option template that this mailbox " + "option is based on", + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AlterField( - model_name='customeruserdatabaseoption', - name='template', + model_name="customeruserdatabaseoption", + name="template", field=models.ForeignKey( - verbose_name='user database option template', - to='hostingpackages.UserDatabaseOption', - help_text='The user database option template that this ' - 'database option is based on', - on_delete=models.CASCADE), + verbose_name="user database option template", + to="hostingpackages.UserDatabaseOption", + help_text="The user database option template that this " + "database option is based on", + on_delete=models.CASCADE, + ), preserve_default=True, ), ] diff --git a/gnuviechadmin/hostingpackages/migrations/0002_auto_20150118_1319.py b/gnuviechadmin/hostingpackages/migrations/0002_auto_20150118_1319.py index 80bf125..420c58e 100644 --- a/gnuviechadmin/hostingpackages/migrations/0002_auto_20150118_1319.py +++ b/gnuviechadmin/hostingpackages/migrations/0002_auto_20150118_1319.py @@ -1,18 +1,15 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations +from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('hostingpackages', '0001_squashed_0005_auto_20150118_1303'), + ("hostingpackages", "0001_squashed_0005_auto_20150118_1303"), ] operations = [ migrations.AlterModelOptions( - name='hostingoption', + name="hostingoption", options={}, ), ] diff --git a/gnuviechadmin/hostingpackages/migrations/0003_auto_20150118_1221.py b/gnuviechadmin/hostingpackages/migrations/0003_auto_20150118_1221.py index c77662e..2a0ff5e 100644 --- a/gnuviechadmin/hostingpackages/migrations/0003_auto_20150118_1221.py +++ b/gnuviechadmin/hostingpackages/migrations/0003_auto_20150118_1221.py @@ -1,24 +1,21 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations +from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('hostingpackages', '0002_auto_20150118_1149'), + ("hostingpackages", "0002_auto_20150118_1149"), ] operations = [ migrations.AlterField( - model_name='customerhostingpackage', - name='name', - field=models.CharField(max_length=128, verbose_name='name'), + model_name="customerhostingpackage", + name="name", + field=models.CharField(max_length=128, verbose_name="name"), preserve_default=True, ), migrations.AlterUniqueTogether( - name='customerhostingpackage', - unique_together=set([('customer', 'name')]), + name="customerhostingpackage", + unique_together=set([("customer", "name")]), ), ] diff --git a/gnuviechadmin/hostingpackages/migrations/0003_auto_20150118_1407.py b/gnuviechadmin/hostingpackages/migrations/0003_auto_20150118_1407.py index 98979c5..a6cbd8a 100644 --- a/gnuviechadmin/hostingpackages/migrations/0003_auto_20150118_1407.py +++ b/gnuviechadmin/hostingpackages/migrations/0003_auto_20150118_1407.py @@ -1,24 +1,23 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('hostingpackages', '0002_auto_20150118_1319'), + ("hostingpackages", "0002_auto_20150118_1319"), ] operations = [ migrations.AlterField( - model_name='customermailboxoption', - name='template', + model_name="customermailboxoption", + name="template", field=models.ForeignKey( - verbose_name='mailbox option template', - to='hostingpackages.MailboxOption', - help_text='The mailbox option template that this mailbox ' - 'option is based on', - on_delete=models.CASCADE), + verbose_name="mailbox option template", + to="hostingpackages.MailboxOption", + help_text="The mailbox option template that this mailbox " + "option is based on", + on_delete=models.CASCADE, + ), preserve_default=True, ), ] diff --git a/gnuviechadmin/hostingpackages/migrations/0004_customerhostingpackage_osuser.py b/gnuviechadmin/hostingpackages/migrations/0004_customerhostingpackage_osuser.py index 616d13f..812ac68 100644 --- a/gnuviechadmin/hostingpackages/migrations/0004_customerhostingpackage_osuser.py +++ b/gnuviechadmin/hostingpackages/migrations/0004_customerhostingpackage_osuser.py @@ -1,22 +1,24 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('osusers', '0004_auto_20150104_1751'), - ('hostingpackages', '0003_auto_20150118_1221'), + ("osusers", "0004_auto_20150104_1751"), + ("hostingpackages", "0003_auto_20150118_1221"), ] operations = [ migrations.AddField( - model_name='customerhostingpackage', - name='osuser', + model_name="customerhostingpackage", + name="osuser", field=models.ForeignKey( - verbose_name='Operating system user', blank=True, - to='osusers.User', null=True, on_delete=models.CASCADE), + verbose_name="Operating system user", + blank=True, + to="osusers.User", + null=True, + on_delete=models.CASCADE, + ), preserve_default=True, ), ] diff --git a/gnuviechadmin/hostingpackages/migrations/0004_customerhostingpackagedomain.py b/gnuviechadmin/hostingpackages/migrations/0004_customerhostingpackagedomain.py index 446c730..82fdbdb 100644 --- a/gnuviechadmin/hostingpackages/migrations/0004_customerhostingpackagedomain.py +++ b/gnuviechadmin/hostingpackages/migrations/0004_customerhostingpackagedomain.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - import django.utils.timezone import model_utils.fields from django.db import migrations, models @@ -8,33 +6,59 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('domains', '0002_auto_20150124_1909'), - ('hostingpackages', '0003_auto_20150118_1407'), + ("domains", "0002_auto_20150124_1909"), + ("hostingpackages", "0003_auto_20150118_1407"), ] operations = [ migrations.CreateModel( - name='CustomerHostingPackageDomain', + name="CustomerHostingPackageDomain", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, auto_created=True, - primary_key=True)), - ('created', model_utils.fields.AutoCreatedField( - default=django.utils.timezone.now, verbose_name='created', - editable=False)), - ('modified', model_utils.fields.AutoLastModifiedField( - default=django.utils.timezone.now, verbose_name='modified', - editable=False)), - ('domain', models.OneToOneField( - verbose_name='hosting domain', to='domains.HostingDomain', - on_delete=models.CASCADE)), - ('hosting_package', models.ForeignKey( - related_name='domains', verbose_name='hosting package', - to='hostingpackages.CustomerHostingPackage', - on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "created", + model_utils.fields.AutoCreatedField( + default=django.utils.timezone.now, + verbose_name="created", + editable=False, + ), + ), + ( + "modified", + model_utils.fields.AutoLastModifiedField( + default=django.utils.timezone.now, + verbose_name="modified", + editable=False, + ), + ), + ( + "domain", + models.OneToOneField( + verbose_name="hosting domain", + to="domains.HostingDomain", + on_delete=models.CASCADE, + ), + ), + ( + "hosting_package", + models.ForeignKey( + related_name="domains", + verbose_name="hosting package", + to="hostingpackages.CustomerHostingPackage", + on_delete=models.CASCADE, + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, bases=(models.Model,), ), diff --git a/gnuviechadmin/hostingpackages/migrations/0005_auto_20150118_1303.py b/gnuviechadmin/hostingpackages/migrations/0005_auto_20150118_1303.py index 9034ae0..88bacc4 100644 --- a/gnuviechadmin/hostingpackages/migrations/0005_auto_20150118_1303.py +++ b/gnuviechadmin/hostingpackages/migrations/0005_auto_20150118_1303.py @@ -1,21 +1,23 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('hostingpackages', '0004_customerhostingpackage_osuser'), + ("hostingpackages", "0004_customerhostingpackage_osuser"), ] operations = [ migrations.AlterField( - model_name='customerhostingpackage', - name='osuser', + model_name="customerhostingpackage", + name="osuser", field=models.OneToOneField( - null=True, blank=True, to='osusers.User', - verbose_name='Operating system user', on_delete=models.CASCADE), + null=True, + blank=True, + to="osusers.User", + verbose_name="Operating system user", + on_delete=models.CASCADE, + ), preserve_default=True, ), ] diff --git a/gnuviechadmin/hostingpackages/migrations/0005_auto_20150125_1508.py b/gnuviechadmin/hostingpackages/migrations/0005_auto_20150125_1508.py index 8bade76..fcf42bd 100644 --- a/gnuviechadmin/hostingpackages/migrations/0005_auto_20150125_1508.py +++ b/gnuviechadmin/hostingpackages/migrations/0005_auto_20150125_1508.py @@ -1,22 +1,19 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations +from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('hostingpackages', '0004_customerhostingpackagedomain'), + ("hostingpackages", "0004_customerhostingpackagedomain"), ] operations = [ migrations.AlterModelOptions( - name='diskspaceoption', + name="diskspaceoption", options={}, ), migrations.AlterUniqueTogether( - name='customerdiskspaceoption', + name="customerdiskspaceoption", unique_together=set([]), ), ] diff --git a/gnuviechadmin/hostingpackages/migrations/0006_auto_20150125_1510.py b/gnuviechadmin/hostingpackages/migrations/0006_auto_20150125_1510.py index 17f24c9..8777347 100644 --- a/gnuviechadmin/hostingpackages/migrations/0006_auto_20150125_1510.py +++ b/gnuviechadmin/hostingpackages/migrations/0006_auto_20150125_1510.py @@ -1,22 +1,19 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations +from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('hostingpackages', '0005_auto_20150125_1508'), + ("hostingpackages", "0005_auto_20150125_1508"), ] operations = [ migrations.AlterModelOptions( - name='userdatabaseoption', + name="userdatabaseoption", options={}, ), migrations.AlterUniqueTogether( - name='customeruserdatabaseoption', + name="customeruserdatabaseoption", unique_together=set([]), ), ] diff --git a/gnuviechadmin/hostingpackages/models.py b/gnuviechadmin/hostingpackages/models.py index c1a5267..496083f 100644 --- a/gnuviechadmin/hostingpackages/models.py +++ b/gnuviechadmin/hostingpackages/models.py @@ -2,21 +2,21 @@ This module contains the hosting package models. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import from django.conf import settings -from django.db import transaction -from django.db import models +from django.core.exceptions import ImproperlyConfigured +from django.db import models, transaction from django.urls import reverse -from django.utils.encoding import python_2_unicode_compatible -from django.utils.translation import ugettext_lazy as _, ungettext - +from django.utils.translation import gettext_lazy as _ +from django.utils.translation import ngettext from model_utils import Choices from model_utils.models import TimeStampedModel from domains.models import HostingDomain from managemails.models import Mailbox -from osusers.models import AdditionalGroup, Group, User as OsUser +from osusers.models import AdditionalGroup, Group +from osusers.models import User as OsUser from userdbs.models import DB_TYPES, UserDatabase DISK_SPACE_UNITS = Choices((0, "M", _("MiB")), (1, "G", _("GiB")), (2, "T", _("TiB"))) @@ -24,7 +24,6 @@ DISK_SPACE_UNITS = Choices((0, "M", _("MiB")), (1, "G", _("GiB")), (2, "T", _("T DISK_SPACE_FACTORS = ((1, None, None), (1024, 1, None), (1024 * 1024, 1024, 1)) -@python_2_unicode_compatible class HostingPackageBase(TimeStampedModel): description = models.TextField(_("description"), blank=True) mailboxcount = models.PositiveIntegerField(_("mailbox count")) @@ -57,7 +56,6 @@ class HostingOption(TimeStampedModel): """ -@python_2_unicode_compatible class DiskSpaceOptionBase(models.Model): diskspace = models.PositiveIntegerField(_("disk space")) diskspace_unit = models.PositiveSmallIntegerField( @@ -87,7 +85,6 @@ class DiskSpaceOption(DiskSpaceOptionBase, HostingOption): unique_together = ["diskspace", "diskspace_unit"] -@python_2_unicode_compatible class UserDatabaseOptionBase(models.Model): number = models.PositiveIntegerField(_("number of databases"), default=1) db_type = models.PositiveSmallIntegerField(_("database type"), choices=DB_TYPES) @@ -99,7 +96,7 @@ class UserDatabaseOptionBase(models.Model): verbose_name_plural = _("Database options") def __str__(self): - return ungettext( + return ngettext( "{type} database", "{count} {type} databases", self.number ).format(type=self.get_db_type_display(), count=self.number) @@ -115,7 +112,6 @@ class UserDatabaseOption(UserDatabaseOptionBase, HostingOption): unique_together = ["number", "db_type"] -@python_2_unicode_compatible class MailboxOptionBase(models.Model): """ Base class for mailbox options. @@ -131,7 +127,7 @@ class MailboxOptionBase(models.Model): verbose_name_plural = _("Mailbox options") def __str__(self): - return ungettext( + return ngettext( "{count} additional mailbox", "{count} additional mailboxes", self.number ).format(count=self.number) @@ -177,7 +173,6 @@ class CustomerHostingPackageManager(models.Manager): return package -@python_2_unicode_compatible class CustomerHostingPackage(HostingPackageBase): """ This class defines customer specific hosting packages. @@ -269,7 +264,7 @@ class CustomerHostingPackage(HostingPackageBase): ) + option.diskspace min_unit = option.diskspace_unit if unit is None: - return DISK_SPACE_FACTORS[min_unit][0] * diskspace * 1024 ** 2 + return DISK_SPACE_FACTORS[min_unit][0] * diskspace * 1024**2 if unit > min_unit: return DISK_SPACE_FACTORS[unit][min_unit] * diskspace return DISK_SPACE_FACTORS[min_unit][unit] * diskspace @@ -287,7 +282,7 @@ class CustomerHostingPackage(HostingPackageBase): """ if unit is None: return ( - DISK_SPACE_FACTORS[self.diskspace_unit][0] * self.diskspace * 1024 ** 2 + DISK_SPACE_FACTORS[self.diskspace_unit][0] * self.diskspace * 1024**2 ) if unit > self.diskspace_unit: return DISK_SPACE_FACTORS[unit][self.diskspace_unit] * self.diskspace @@ -376,13 +371,17 @@ class CustomerHostingPackage(HostingPackageBase): self.copy_template_attributes() self.osuser = OsUser.objects.create_user(self.customer) for group in settings.OSUSER_DEFAULT_GROUPS: - AdditionalGroup.objects.create( - user=self.osuser, group=Group.objects.get(groupname=group) - ) + try: + AdditionalGroup.objects.create( + user=self.osuser, group=Group.objects.get(groupname=group) + ) + except Group.DoesNotExist as e: + raise ImproperlyConfigured( + f"group {group} has not been defined" + ) from e return super(CustomerHostingPackage, self).save(*args, **kwargs) -@python_2_unicode_compatible class CustomerHostingPackageDomain(TimeStampedModel): """ This class defines the relationship from a hosting package to a hosting diff --git a/gnuviechadmin/hostingpackages/templates/hostingpackages/add_hosting_option.html b/gnuviechadmin/hostingpackages/templates/hostingpackages/add_hosting_option.html new file mode 100644 index 0000000..172234c --- /dev/null +++ b/gnuviechadmin/hostingpackages/templates/hostingpackages/add_hosting_option.html @@ -0,0 +1,13 @@ +{% extends "hostingpackages/base.html" %} +{% load i18n crispy_forms_tags %} +{% block title %}{{ block.super }} - + {% blocktranslate with package=hostingpackage.name full_name=customer.get_full_name trimmed %} + Add Option to Hosting Package {{ package }} of Customer {{ full_name }} + {% endblocktranslate %}{% endblock title %} +{% block page_title %}{% blocktranslate with package=hostingpackage.name full_name=customer.get_full_name trimmed %} + Add Option to Hosting Package {{ package }} of Customer {{ full_name }} +{% endblocktranslate %}{% endblock page_title %} + +{% block content %} + {% crispy form %} +{% endblock content %} diff --git a/gnuviechadmin/templates/hostingpackages/base.html b/gnuviechadmin/hostingpackages/templates/hostingpackages/base.html similarity index 100% rename from gnuviechadmin/templates/hostingpackages/base.html rename to gnuviechadmin/hostingpackages/templates/hostingpackages/base.html diff --git a/gnuviechadmin/hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html b/gnuviechadmin/hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html new file mode 100644 index 0000000..cf13634 --- /dev/null +++ b/gnuviechadmin/hostingpackages/templates/hostingpackages/customerhostingpackage_admin_list.html @@ -0,0 +1,53 @@ +{% extends "hostingpackages/base.html" %} +{% load i18n %} +{% block title %}{{ block.super }} - {% translate "All hosting packages" %}{% endblock title %} +{% block page_title %}{% translate "All hosting packages" %}{% endblock page_title %} + +{% block content %} + {% if customerhostingpackage_list %} + + + + + + + + + + + + + + {% for package in customerhostingpackage_list %} + + + + + + + + + + {% endfor %} + +
{% translate "Name" %}{% translate "Customer" %}{% translate "OS User" %}{% translate "Disk space" %}{% translate "Mailboxes" %}{% translate "Databases" %}{% translate "Setup date" %}
{{ package.name }}{{ package.customer }}{{ package.osuser.username }} + {% with diskspace=package.get_disk_space %} + {{ diskspace|filesizeformat }} + {% endwith %} + + {% blocktranslate with num=package.used_mailbox_count total=package.mailbox_count trimmed %} + used {{ num }} of {{ total }} + {% endblocktranslate %}{% 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 %}{{ package.created }}
+ {% else %} +

{% translate "No hosting packages have been setup yet." %}

+ {% endif %} +

+ {% translate "Add hosting package" %} +

+{% endblock content %} \ No newline at end of file diff --git a/gnuviechadmin/hostingpackages/templates/hostingpackages/customerhostingpackage_create.html b/gnuviechadmin/hostingpackages/templates/hostingpackages/customerhostingpackage_create.html new file mode 100644 index 0000000..8134bd9 --- /dev/null +++ b/gnuviechadmin/hostingpackages/templates/hostingpackages/customerhostingpackage_create.html @@ -0,0 +1,12 @@ +{% extends "hostingpackages/base.html" %} +{% load i18n crispy_forms_tags %} +{% block title %}{{ block.super }} - {% blocktranslate with full_name=customer.get_full_name trimmed %} + Add hosting package for Customer {{ full_name }} +{% endblocktranslate %}{% endblock title %} +{% block page_title %}{% blocktranslate with full_name=customer.get_full_name trimmed %} + Add Hosting Package for Customer {{ full_name }} +{% endblocktranslate %}{% endblock page_title %} + +{% block content %} + {% crispy form %} +{% endblock content %} diff --git a/gnuviechadmin/hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html b/gnuviechadmin/hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html new file mode 100644 index 0000000..c04be76 --- /dev/null +++ b/gnuviechadmin/hostingpackages/templates/hostingpackages/customerhostingpackage_detail.html @@ -0,0 +1,280 @@ +{% extends "hostingpackages/base.html" %} +{% load i18n %} + +{% block title %}{{ block.super }} - {% spaceless %} + {% if user == customer %} + {% blocktranslate with package=hostingpackage.name trimmed %} + Details for your Hosting Package {{ package }} + {% endblocktranslate %} + {% else %} + {% blocktranslate with package=hostingpackage.name full_name=customer.get_full_name trimmed %} + Details for Hosting Package {{ package }} of {{ full_name }} + {% endblocktranslate %} + {% endif %} +{% endspaceless %}{% endblock title %} + +{% block page_title %}{% blocktranslate with package=hostingpackage.name trimmed %} + Details of Hosting Package {{ package }} +{% endblocktranslate %}{% endblock page_title %} + +{% block content %} +
+
+
+
+ {% translate "Hosting Package Information" %} + {% if user.is_staff %} +
+ +
+ {% endif %} +
+
+
+
{% translate "Name" %}
+
{{ hostingpackage.name }}
+
{% translate "Description" %}
+
{{ hostingpackage.description|default:"-" }}
+
{% translate "Disk space" %}
+ {% with diskspace=hostingpackage.get_disk_space packagespace=hostingpackage.get_package_space %} +
+ {{ diskspace|filesizeformat }} + +
+ {% endwith %} +
{% translate "Mailboxes" %}
+
+ {% blocktranslate with num=hostingpackage.used_mailbox_count total=hostingpackage.mailbox_count trimmed %} + {{ num }} of {{ total }} in use{% endblocktranslate %} + +
+
{% if osuser.is_sftp_user %}{% translate "SFTP username" %}{% else %} + {% translate "SSH/SFTP username" %}{% endif %}
+
{{ osuser.username }}{% if sshkeys %} + {{ sshkeys|length }}{% endif %}
+
{% translate "Upload server" %}
+
{{ uploadserver }}
+
+
+
+
+
+
+
{% translate "Hosting Package Options" %}
+ {% if hostingoptions %} +
    + {% for opt in hostingoptions %} +
  • {{ opt }}
  • + {% endfor %} +
+ {% else %} +
{% translate "No options booked" %}
+ {% endif %} + {% if user.is_staff %} + + {% endif %} +
+
+ +
+
+
+
+
{% translate "Domains" %}
+ {% if domains %} + + + + + + + + + + + {% for domain in domains %} + + + {% if domain.domain.maildomain.mailaddress_set.exists %} + + {% else %} + + {% endif %} + {% if domain.domain.website_set.exists %} + + {% else %} + + {% endif %} + + + {% endfor %} + +
{% translate "Domain name" %}{% translate "Mail addresses" %}{% translate "Websites" %}{% translate "Actions" %}
{{ domain.domain }} + {% with maildomain=domain.domain.maildomain %} + {% for mailaddress in maildomain.mailaddresses %}{% spaceless %} + {{ mailaddress }} + {% translate "Delete mail address" %} + {% endspaceless %}{% if not forloop.last %}, {% endif %}{% endfor %} + {% endwith %} + {% translate "None" %} + {% with domain=domain.domain %} + {% for website in domain.website_set.all %}{% spaceless %} + {{ website }} + {% translate "Delete website" %} + {% endspaceless %}{% if not forloop.last %}, {% endif %}{% endfor %} + {% endwith %} + {% translate "None" %} + {% if domain.domain.maildomain %} + {% with maildomain=domain.domain.maildomain %} + {% translate "Add mail address" %} + {% endwith %} + {% endif %} + {% with hostingdomain=domain.domain %} + {% translate "Add website" %} + {% endwith %} +
+ {% else %} +
{% translate "There are no domains assigned to this hosting package yet." %}
+ {% endif %} + {% if user.is_staff %} + + {% endif %} +
+
+
+
+
+
+
{% translate "E-Mail-Accounts" %}
+ {% if mailboxes %} + + + + + + + + + + + {% for mailbox in mailboxes %} + + + + + + {% endfor %} + +
{% translate "Mailbox" %}{% translate "Mail addresses" %}{% translate "Active" %}{% translate "Actions" %}
{{ mailbox.username }}{{ mailbox.mailaddresses|join:", " }} + {% if mailbox.active %}{% translate "Active" %}{% else %} + {% translate "inactive" %}{% endif %} + {% translate "Set mailbox password" %} +
+ {% else %} +
{% translate "There are no mailboxes assigned to this hosting package yet." %}
+ {% endif %} + {% if hostingpackage.may_add_mailbox %} + + {% endif %} +
+
+
+
+
{% translate "Databases" %}
+ {% if databases %} + + + + + + + + + + + {% for database in databases %} + + + + + + + {% endfor %} + +
{% translate "Database name" %}{% translate "Database user" %}{% translate "Type" %}{% translate "Actions" %}
{{ database.db_name }}{{ database.db_user.name }}{% include "userdbs/snippets/db_type.html" with db_type=database.db_user.db_type %} + {% translate "Set database user password" %} + {% translate "Delete database" %} +
+ {% else %} +
{% translate "There are no databases assigned to this hosting package yet." %}
+ {% endif %} + {% if hostingpackage.may_add_database %} + + {% endif %} +
+
+
+{% endblock content %} diff --git a/gnuviechadmin/hostingpackages/templates/hostingpackages/customerhostingpackage_option_choices.html b/gnuviechadmin/hostingpackages/templates/hostingpackages/customerhostingpackage_option_choices.html new file mode 100644 index 0000000..3da2568 --- /dev/null +++ b/gnuviechadmin/hostingpackages/templates/hostingpackages/customerhostingpackage_option_choices.html @@ -0,0 +1,29 @@ +{% extends "hostingpackages/base.html" %} +{% load i18n %} + +{% block title %}{{ block.super }} - + {% blocktranslate with package=hostingpackage.name full_name=customer.get_full_name trimmed %}Choose new Option for + Hosting Package {{ package }} of Customer {{ full_name }}{% endblocktranslate %}{% endblock title %} + +{% block page_title %}{% blocktranslate with package=hostingpackage.name full_name=customer.get_full_name trimmed %} + Choose new Option for Hosting Package {{ package }} of Customer {{ full_name }} +{% endblocktranslate %}{% endblock page_title %} + +{% block content %} +
+ {% for label, items in hosting_options %} +
+
+
{{ label }}
+
    + {% for item, option_type in items %} +
  • {{ item }}
  • + {% endfor %} +
+
+
+ {% endfor %} +
+{% endblock %} \ No newline at end of file diff --git a/gnuviechadmin/hostingpackages/tests/test_models.py b/gnuviechadmin/hostingpackages/tests/test_models.py index 66f579c..5db3c20 100644 --- a/gnuviechadmin/hostingpackages/tests/test_models.py +++ b/gnuviechadmin/hostingpackages/tests/test_models.py @@ -2,10 +2,17 @@ Test for models. """ +from django.contrib.auth import get_user_model +from django.core.exceptions import ImproperlyConfigured +from django.test import TestCase, override_settings -from django.test import TestCase +from hostingpackages.models import ( + DISK_SPACE_UNITS, + CustomerHostingPackage, + HostingPackageTemplate, +) -from hostingpackages.models import DISK_SPACE_UNITS, CustomerHostingPackage +User = get_user_model() class CustomerHostingPackageTest(TestCase): @@ -13,7 +20,7 @@ class CustomerHostingPackageTest(TestCase): package = CustomerHostingPackage( diskspace=10, diskspace_unit=DISK_SPACE_UNITS.G ) - self.assertEqual(package.get_disk_space(), 10 * 1024 ** 3) + self.assertEqual(package.get_disk_space(), 10 * 1024**3) def test_get_disk_space_mib(self): package = CustomerHostingPackage( @@ -26,3 +33,20 @@ class CustomerHostingPackageTest(TestCase): diskspace=256, diskspace_unit=DISK_SPACE_UNITS.M ) self.assertEqual(package.get_quota(), (262144, 275251)) + + @override_settings(OSUSER_DEFAULT_GROUPS=["testgroup"]) + def test_additional_group_not_defined(self): + user = User.objects.create(username="test") + template = HostingPackageTemplate.objects.create( + description="Test package 1 - Description", + mailboxcount=10, + diskspace=100, + diskspace_unit=DISK_SPACE_UNITS.M, + name="Test package 1", + ) + with self.assertRaises(ImproperlyConfigured) as ctx: + package = CustomerHostingPackage.objects.create_from_template( + customer=user, template=template, name="Test customer package" + ) + package.save() + self.assertIn("testgroup", str(ctx.exception)) diff --git a/gnuviechadmin/hostingpackages/urls.py b/gnuviechadmin/hostingpackages/urls.py index 4d0201b..fe3faa6 100644 --- a/gnuviechadmin/hostingpackages/urls.py +++ b/gnuviechadmin/hostingpackages/urls.py @@ -2,9 +2,9 @@ This module defines the URL patterns for hosting package related views. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import -from django.conf.urls import url +from django.urls import re_path from .views import ( AddHostingOption, @@ -12,26 +12,34 @@ from .views import ( CreateCustomerHostingPackage, CreateHostingPackage, CustomerHostingPackageDetails, - CustomerHostingPackageList, HostingOptionChoices, ) - urlpatterns = [ - url(r'^create$', CreateHostingPackage.as_view(), - name='create_hosting_package'), - url(r'^allpackages/', - AllCustomerHostingPackageList.as_view(), name='all_hosting_packages'), - url(r'^(?P[-\w0-9@.+_]+)/$', - CustomerHostingPackageList.as_view(), name='hosting_packages'), - url(r'^(?P[-\w0-9@.+_]+)/create$', + re_path(r"^create$", CreateHostingPackage.as_view(), name="create_hosting_package"), + re_path( + r"^allpackages/", + AllCustomerHostingPackageList.as_view(), + name="all_hosting_packages", + ), + re_path( + r"^(?P[-\w0-9@.+_]+)/create$", CreateCustomerHostingPackage.as_view(), - name='create_customer_hosting_package'), - url(r'^(?P[-\w0-9@.+_]+)/(?P\d+)/$', + name="create_customer_hosting_package", + ), + re_path( + r"^(?P[-\w0-9@.+_]+)/(?P\d+)/$", CustomerHostingPackageDetails.as_view(), - name='hosting_package_details'), - url(r'^(?P\d+)/option-choices$', - HostingOptionChoices.as_view(), name='hosting_option_choices'), - url(r'^(?P\d+)/add-option/(?P\w+)/(?P\d+)$', - AddHostingOption.as_view(), name='add_hosting_option'), + name="hosting_package_details", + ), + re_path( + r"^(?P\d+)/option-choices$", + HostingOptionChoices.as_view(), + name="hosting_option_choices", + ), + re_path( + r"^(?P\d+)/add-option/(?P\w+)/(?P\d+)$", + AddHostingOption.as_view(), + name="add_hosting_option", + ), ] diff --git a/gnuviechadmin/hostingpackages/views.py b/gnuviechadmin/hostingpackages/views.py index d07f4cc..37ff280 100644 --- a/gnuviechadmin/hostingpackages/views.py +++ b/gnuviechadmin/hostingpackages/views.py @@ -2,28 +2,17 @@ This module defines views related to hosting packages. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import from django.conf import settings -from django.http import Http404 -from django.shortcuts import redirect, get_object_or_404 -from django.utils.translation import ugettext as _ -from django.views.generic import ( - DetailView, - ListView, -) -from django.views.generic.edit import ( - CreateView, - FormView, -) from django.contrib import messages from django.contrib.auth import get_user_model - -from braces.views import ( - LoginRequiredMixin, - StaffuserRequiredMixin, -) - +from django.contrib.auth.mixins import PermissionRequiredMixin, UserPassesTestMixin +from django.http import Http404 +from django.shortcuts import get_object_or_404, redirect +from django.utils.translation import gettext as _ +from django.views.generic import DetailView, ListView +from django.views.generic.edit import CreateView, FormView from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin from .forms import ( @@ -41,26 +30,27 @@ from .models import ( ) -class CreateHostingPackage( - LoginRequiredMixin, StaffuserRequiredMixin, CreateView -): +class CreateHostingPackage(PermissionRequiredMixin, CreateView): """ Create a hosting package. """ + model = CustomerHostingPackage raise_exception = True - template_name_suffix = '_create' + permission_required = "domains.add_customerhostingpackage" + template_name_suffix = "_create" form_class = CreateHostingPackageForm def form_valid(self, form): - hostingpackage = form.save() + hosting_package = form.save() messages.success( self.request, - _('Started setup of new hosting package {name}.').format( - name=hostingpackage.name) + _("Started setup of new hosting package {name}.").format( + name=hosting_package.name + ), ) - return redirect(hostingpackage) + return redirect(hosting_package) class CreateCustomerHostingPackage(CreateHostingPackage): @@ -68,6 +58,7 @@ class CreateCustomerHostingPackage(CreateHostingPackage): Create a hosting package for a selected customer. """ + form_class = CreateCustomerHostingPackageForm def get_form_kwargs(self): @@ -76,25 +67,24 @@ class CreateCustomerHostingPackage(CreateHostingPackage): return kwargs def get_customer_object(self): - return get_object_or_404( - get_user_model(), username=self.kwargs['user']) + return get_object_or_404(get_user_model(), username=self.kwargs["user"]) def get_context_data(self, **kwargs): - context = super( - CreateCustomerHostingPackage, self).get_context_data(**kwargs) - context['customer'] = self.get_customer_object() + context = super(CreateCustomerHostingPackage, self).get_context_data(**kwargs) + context["customer"] = self.get_customer_object() return context def form_valid(self, form): - hostingpackage = form.save(commit=False) - hostingpackage.customer = self.get_customer_object() - hostingpackage.save() + hosting_package = form.save(commit=False) + hosting_package.customer = self.get_customer_object() + hosting_package.save() messages.success( self.request, - _('Started setup of new hosting package {name}.').format( - name=hostingpackage.name) + _("Started setup of new hosting package {name}.").format( + name=hosting_package.name + ), ) - return redirect(hostingpackage) + return redirect(hosting_package) class CustomerHostingPackageDetails(StaffOrSelfLoginRequiredMixin, DetailView): @@ -102,156 +92,161 @@ class CustomerHostingPackageDetails(StaffOrSelfLoginRequiredMixin, DetailView): This view is for showing details of a customer hosting package. """ + model = CustomerHostingPackage - context_object_name = 'hostingpackage' + context_object_name = "hostingpackage" customer = None def get_customer_object(self): if self.customer is None: self.customer = get_object_or_404( - get_user_model(), username=self.kwargs['user']) + get_user_model(), username=self.kwargs["user"] + ) return self.customer def get_context_data(self, **kwargs): - context = super(CustomerHostingPackageDetails, self).get_context_data( - **kwargs) - context.update({ - 'customer': self.get_customer_object(), - 'uploadserver': settings.OSUSER_UPLOAD_SERVER, - 'databases': context['hostingpackage'].databases, - 'osuser': context['hostingpackage'].osuser, - 'hostingoptions': - context['hostingpackage'].get_hostingoptions(), - 'domains': context['hostingpackage'].domains.all(), - 'mailboxes': context['hostingpackage'].mailboxes, - }) - context['sshkeys'] = context['osuser'].sshpublickey_set.all() + context = super(CustomerHostingPackageDetails, self).get_context_data(**kwargs) + context.update( + { + "customer": self.get_customer_object(), + "uploadserver": settings.OSUSER_UPLOAD_SERVER, + "databases": context["hostingpackage"].databases, + "osuser": context["hostingpackage"].osuser, + "hostingoptions": context["hostingpackage"].get_hostingoptions(), + "domains": context["hostingpackage"].domains.all(), + "mailboxes": context["hostingpackage"].mailboxes, + } + ) + context["sshkeys"] = context["osuser"].sshpublickey_set.all() return context -class AllCustomerHostingPackageList( - LoginRequiredMixin, StaffuserRequiredMixin, ListView -): +class StaffUserRequiredMixin(UserPassesTestMixin): + """ + Mixin to make views available to staff members only. + + """ + + def test_func(self): + return self.request.user.is_staff + + +class AllCustomerHostingPackageList(StaffUserRequiredMixin, ListView): """ This view is used for showing a list of all hosting packages. """ + model = CustomerHostingPackage - template_name_suffix = '_admin_list' - - -class CustomerHostingPackageList(StaffOrSelfLoginRequiredMixin, ListView): - """ - This view is used for showing a list of a customer's hosting packages. - - """ - model = CustomerHostingPackage - customer = None - - def get_customer_object(self): - if self.customer is None: - self.customer = get_object_or_404( - get_user_model(), username=self.kwargs['user']) - return self.customer - - def get_context_data(self, **kwargs): - context = super(CustomerHostingPackageList, self).get_context_data( - **kwargs) - context['customer'] = self.get_customer_object() - return context + template_name_suffix = "_admin_list" def get_queryset(self): - return super(CustomerHostingPackageList, self).get_queryset().filter( - customer__username=self.kwargs['user']) + return ( + super() + .get_queryset() + .select_related("osuser", "customer") + .only("name", "pk", "created", "customer__username", "osuser__username") + ) -class HostingOptionChoices( - LoginRequiredMixin, StaffuserRequiredMixin, DetailView -): +class HostingOptionChoices(StaffUserRequiredMixin, DetailView): """ This view displays choices of hosting options for a customer hosting package. """ + model = CustomerHostingPackage - context_object_name = 'hostingpackage' - template_name_suffix = '_option_choices' + context_object_name = "hostingpackage" + template_name_suffix = "_option_choices" def get_context_data(self, **kwargs): - context = super(HostingOptionChoices, self).get_context_data( - **kwargs) - context.update({ - 'customer': self.get_object().customer, - 'hosting_options': ( - (_('Disk space'), - [(option, 'diskspace') for option in - DiskSpaceOption.objects.all()]), - (_('Mailboxes'), - [(option, 'mailboxes') for option in - MailboxOption.objects.all()]), - (_('Databases'), - [(option, 'databases') for option in - UserDatabaseOption.objects.all()]), - ), - }) + context = super(HostingOptionChoices, self).get_context_data(**kwargs) + context.update( + { + "customer": self.get_object().customer, + "hosting_options": ( + ( + _("Disk space"), + [ + (option, "diskspace") + for option in DiskSpaceOption.objects.all() + ], + ), + ( + _("Mailboxes"), + [ + (option, "mailboxes") + for option in MailboxOption.objects.all() + ], + ), + ( + _("Databases"), + [ + (option, "databases") + for option in UserDatabaseOption.objects.all() + ], + ), + ), + } + ) return context -class AddHostingOption( - LoginRequiredMixin, StaffuserRequiredMixin, FormView -): - template_name = 'hostingpackages/add_hosting_option.html' +class AddHostingOption(StaffUserRequiredMixin, FormView): + template_name = "hostingpackages/add_hosting_option.html" def get_form_class(self): - optiontype = self.kwargs['type'] - if optiontype == 'diskspace': + optiontype = self.kwargs["type"] + if optiontype == "diskspace": return AddDiskspaceOptionForm - elif optiontype == 'mailboxes': + elif optiontype == "mailboxes": return AddMailboxOptionForm - elif optiontype == 'databases': + elif optiontype == "databases": return AddUserDatabaseOptionForm raise Http404() def get_hosting_package(self): - return get_object_or_404( - CustomerHostingPackage, pk=int(self.kwargs['package'])) + return get_object_or_404(CustomerHostingPackage, pk=int(self.kwargs["package"])) def get_option_template(self): - optiontype = self.kwargs['type'] - optionid = int(self.kwargs['optionid']) - if optiontype == 'diskspace': + optiontype = self.kwargs["type"] + optionid = int(self.kwargs["optionid"]) + if optiontype == "diskspace": return get_object_or_404(DiskSpaceOption, pk=optionid) - elif optiontype == 'mailboxes': + elif optiontype == "mailboxes": return get_object_or_404(MailboxOption, pk=optionid) - elif optiontype == 'databases': + elif optiontype == "databases": return get_object_or_404(UserDatabaseOption, pk=optionid) raise Http404() def get_form_kwargs(self): kwargs = super(AddHostingOption, self).get_form_kwargs() - kwargs['hostingpackage'] = self.get_hosting_package() - kwargs['option_template'] = self.get_option_template() + kwargs["hostingpackage"] = self.get_hosting_package() + kwargs["option_template"] = self.get_option_template() return kwargs def get_initial(self): initial = super(AddHostingOption, self).get_initial() template = self.get_option_template() if type(template) == DiskSpaceOption: - initial.update({ - 'diskspace': template.diskspace, - 'diskspace_unit': template.diskspace_unit, - }) + initial.update( + { + "diskspace": template.diskspace, + "diskspace_unit": template.diskspace_unit, + } + ) elif type(template) == MailboxOption: - initial['number'] = template.number + initial["number"] = template.number elif type(template) == UserDatabaseOption: - initial['number'] = template.number + initial["number"] = template.number else: raise Http404() return initial def get_context_data(self, **kwargs): context = super(AddHostingOption, self).get_context_data(**kwargs) - context['option_template'] = self.get_option_template() + context["option_template"] = self.get_option_template() return context def form_valid(self, form): @@ -259,8 +254,8 @@ class AddHostingOption( hosting_package = self.get_hosting_package() messages.success( self.request, - _("Successfully added option {option} to hosting package " - "{package}.").format( - option=option, package=hosting_package.name) + _( + "Successfully added option {option} to hosting package " "{package}." + ).format(option=option, package=hosting_package.name), ) return redirect(hosting_package) diff --git a/gnuviechadmin/invoices/__init__.py b/gnuviechadmin/invoices/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gnuviechadmin/invoices/admin.py b/gnuviechadmin/invoices/admin.py new file mode 100644 index 0000000..65cfb5f --- /dev/null +++ b/gnuviechadmin/invoices/admin.py @@ -0,0 +1,14 @@ +from django.contrib import admin + +from invoices.models import Invoice + + +class InvoiceAdmin(admin.ModelAdmin): + list_display = ["invoice_number", "customer"] + readonly_fields = ["customer"] + + def has_add_permission(self, request): + return False + + +admin.site.register(Invoice, InvoiceAdmin) diff --git a/gnuviechadmin/invoices/apps.py b/gnuviechadmin/invoices/apps.py new file mode 100644 index 0000000..9062add --- /dev/null +++ b/gnuviechadmin/invoices/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig +from django.utils.translation import ugettext_lazy as _ + + +class InvoiceConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "invoices" + verbose_name = _("Invoices") diff --git a/gnuviechadmin/invoices/locale/de/LC_MESSAGES/django.po b/gnuviechadmin/invoices/locale/de/LC_MESSAGES/django.po new file mode 100644 index 0000000..0e4e652 --- /dev/null +++ b/gnuviechadmin/invoices/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,69 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , 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 \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}" diff --git a/gnuviechadmin/invoices/migrations/0001_initial_invoice_model.py b/gnuviechadmin/invoices/migrations/0001_initial_invoice_model.py new file mode 100644 index 0000000..3d36e9c --- /dev/null +++ b/gnuviechadmin/invoices/migrations/0001_initial_invoice_model.py @@ -0,0 +1,38 @@ +# Generated by Django 3.2.18 on 2023-04-23 10:37 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import invoices.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Invoice', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('invoice', models.FileField(upload_to=invoices.models.customer_invoice_path)), + ('invoice_number', models.SlugField(max_length=10, unique=True, verbose_name='Invoice number')), + ('invoice_date', models.DateField(verbose_name='Invoice date')), + ('invoice_value', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Amount')), + ('invoice_currency', models.PositiveSmallIntegerField(choices=[(1, 'EUR')], verbose_name='Currency')), + ('due_date', models.DateField(verbose_name='Due date')), + ('payment_date', models.DateField(blank=True, null=True, verbose_name='Payment date')), + ('payment_variant', models.TextField(blank=True, null=True, verbose_name='Payment variant')), + ('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='customer')), + ], + options={ + 'verbose_name': 'Invoice', + 'verbose_name_plural': 'Invoices', + 'ordering': ['-invoice_date', 'customer'], + }, + ), + ] diff --git a/gnuviechadmin/invoices/migrations/0002_refine_invoice_model.py b/gnuviechadmin/invoices/migrations/0002_refine_invoice_model.py new file mode 100644 index 0000000..1326269 --- /dev/null +++ b/gnuviechadmin/invoices/migrations/0002_refine_invoice_model.py @@ -0,0 +1,59 @@ +# Generated by Django 3.2.18 on 2023-04-23 12:15 + +import django.core.validators +from django.db import migrations, models +import invoices.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('invoices', '0001_initial_invoice_model'), + ] + + operations = [ + migrations.AlterModelOptions( + name='invoice', + options={'ordering': ['-invoice_date', 'customer'], 'verbose_name': 'invoice', 'verbose_name_plural': 'invoices'}, + ), + migrations.AlterField( + model_name='invoice', + name='due_date', + field=models.DateField(verbose_name='due date'), + ), + migrations.AlterField( + model_name='invoice', + name='invoice', + field=models.FileField(upload_to=invoices.models.customer_invoice_path, validators=[invoices.models.validate_pdf, django.core.validators.FileExtensionValidator(allowed_extensions=['pdf'])]), + ), + migrations.AlterField( + model_name='invoice', + name='invoice_currency', + field=models.PositiveSmallIntegerField(choices=[(1, 'EUR')], verbose_name='currency'), + ), + migrations.AlterField( + model_name='invoice', + name='invoice_date', + field=models.DateField(verbose_name='invoice date'), + ), + migrations.AlterField( + model_name='invoice', + name='invoice_number', + field=models.SlugField(max_length=10, unique=True, verbose_name='invoice number'), + ), + migrations.AlterField( + model_name='invoice', + name='invoice_value', + field=models.DecimalField(decimal_places=2, max_digits=10, verbose_name='amount'), + ), + migrations.AlterField( + model_name='invoice', + name='payment_date', + field=models.DateField(blank=True, null=True, verbose_name='payment date'), + ), + migrations.AlterField( + model_name='invoice', + name='payment_variant', + field=models.TextField(blank=True, null=True, verbose_name='payment variant'), + ), + ] diff --git a/gnuviechadmin/invoices/migrations/__init__.py b/gnuviechadmin/invoices/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gnuviechadmin/invoices/models.py b/gnuviechadmin/invoices/models.py new file mode 100644 index 0000000..8d600d3 --- /dev/null +++ b/gnuviechadmin/invoices/models.py @@ -0,0 +1,56 @@ +import magic +from django.conf import settings +from django.core.exceptions import ValidationError +from django.core.validators import FileExtensionValidator +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +CURRENCIES = [(1, "EUR")] + + +def customer_invoice_path(instance, filename): + return "invoices/{0}/{1}.pdf".format( + instance.customer.username, instance.invoice_number + ) + + +def validate_pdf(value): + valid_mime_types = ["application/pdf"] + file_mime_type = magic.from_buffer(value.read(1024), mime=True) + if file_mime_type not in valid_mime_types: + raise ValidationError("Unsupported file type.") + + +class Invoice(models.Model): + customer = models.ForeignKey( + settings.AUTH_USER_MODEL, verbose_name=_("customer"), on_delete=models.CASCADE + ) + invoice = models.FileField( + upload_to=customer_invoice_path, + validators=[validate_pdf, FileExtensionValidator(allowed_extensions=["pdf"])], + ) + invoice_number = models.SlugField( + verbose_name=_("invoice number"), max_length=10, unique=True + ) + invoice_date = models.DateField(verbose_name=_("invoice date")) + invoice_value = models.DecimalField( + verbose_name=_("amount"), decimal_places=2, max_digits=10 + ) + invoice_currency = models.PositiveSmallIntegerField( + verbose_name=_("currency"), choices=CURRENCIES + ) + due_date = models.DateField(verbose_name=_("due date")) + payment_date = models.DateField( + verbose_name=_("payment date"), blank=True, null=True + ) + payment_variant = models.TextField( + verbose_name=_("payment variant"), blank=True, null=True + ) + + class Meta: + verbose_name = _("invoice") + verbose_name_plural = _("invoices") + ordering = ["-invoice_date", "customer"] + + def __str__(self): + return _("Invoice {0}").format(self.invoice_number) diff --git a/gnuviechadmin/invoices/serializers.py b/gnuviechadmin/invoices/serializers.py new file mode 100644 index 0000000..8506a78 --- /dev/null +++ b/gnuviechadmin/invoices/serializers.py @@ -0,0 +1,33 @@ +from django.contrib.auth import get_user_model +from rest_framework import serializers + +from invoices.models import Invoice + +User = get_user_model() + + +class InvoiceSerializer(serializers.ModelSerializer): + customer = serializers.SlugRelatedField( + queryset=User.objects.all(), slug_field="username" + ) + + class Meta: + model = Invoice + fields = [ + "url", + "customer", + "invoice", + "invoice_number", + "invoice_date", + "invoice_value", + "invoice_currency", + "due_date", + "payment_date", + "payment_variant", + ] + extra_kwargs = { + "url": { + "lookup_field": "invoice_number", + "lookup_url_kwarg": "invoice_number", + }, + } diff --git a/gnuviechadmin/invoices/tests.py b/gnuviechadmin/invoices/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/gnuviechadmin/invoices/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/gnuviechadmin/invoices/views.py b/gnuviechadmin/invoices/views.py new file mode 100644 index 0000000..60d27d1 --- /dev/null +++ b/gnuviechadmin/invoices/views.py @@ -0,0 +1,28 @@ +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" diff --git a/gnuviechadmin/locale/de/LC_MESSAGES/django.po b/gnuviechadmin/locale/de/LC_MESSAGES/django.po index b67e270..a2fe68d 100644 --- a/gnuviechadmin/locale/de/LC_MESSAGES/django.po +++ b/gnuviechadmin/locale/de/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: gnuviechadmin\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-01-29 11:04+0100\n" -"PO-Revision-Date: 2015-02-01 19:04+0100\n" +"POT-Creation-Date: 2023-04-22 13:01+0200\n" +"PO-Revision-Date: 2023-04-22 12:58+0200\n" "Last-Translator: Jan Dittberner \n" "Language-Team: Jan Dittberner \n" "Language: de\n" @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.6.10\n" +"X-Generator: Poedit 3.2.2\n" "X-Poedit-SourceCharset: UTF-8\n" #: templates/account/account_inactive.html:4 @@ -44,7 +44,7 @@ msgstr "Die folgenden E-Mailadressen sind Ihrem Konto zugeordnet:" msgid "Email address" msgstr "E-Mailadresse" -#: templates/account/email.html:15 templates/account/email.html.py:25 +#: templates/account/email.html:15 templates/account/email.html:25 msgid "Verified" msgstr "Geprüft" @@ -68,7 +68,7 @@ msgstr "Als primär definieren" msgid "Re-send Verification" msgstr "Prüf-E-Mail noch einmal verschicken" -#: templates/account/email.html:45 templates/socialaccount/connections.html:36 +#: templates/account/email.html:45 templates/socialaccount/connections.html:42 msgid "Remove" msgstr "Entfernen" @@ -158,15 +158,15 @@ msgstr "" "Bitte bestätigen Sie, dass %(email)s eine E-" "Mailadresse des Benutzers %(user_display)s ist." -#: templates/account/email_confirm.html:12 +#: templates/account/email_confirm.html:14 msgid "Confirm" msgstr "Bestätigen" -#: templates/account/email_confirm.html:16 +#: templates/account/email_confirm.html:18 #, python-format msgid "" -"This e-mail confirmation link expired or is invalid. Please issue a new e-mail confirmation request." +"This e-mail confirmation link expired or is invalid. Please issue a new e-mail confirmation request." msgstr "" "Dieser E-Mail-Bestätigungslink ist abgelaufen oder ungültig. Bitte stellen " "Sie eine neue E-Mail-Bestätigungsanfrage." @@ -180,42 +180,17 @@ msgstr "" "Sie haben bestätigt, dass %(email)s eine E-" "Mailadresse des Benutzers %(user_display)s ist." -#: templates/account/login.html:4 templates/account/login.html.py:5 -#: templates/account/login.html:30 templates/base.html.py:81 -#: templates/registration/login.html:4 +#: templates/account/login.html:4 templates/account/login.html:5 +#: templates/account/login.html:18 templates/base.html:124 +#: templates/registration/login.html:4 templates/socialaccount/login.html:4 msgid "Sign In" msgstr "Anmelden" -#: templates/account/login.html:10 -#, python-format -msgid "" -"Please sign in with one\n" -"of your existing third party accounts. Or, sign " -"up\n" -"for a %(site_name)s account and sign in below:" -msgstr "" -"Bitte melden Sie sich mit Ihrem bestehenden Drittanbieterkonto an. Oder, registrieren Sie sich für ein Konto auf " -"%(site_name)s und melden Sie sich unten an:" - #: templates/account/login.html:17 -msgid "or" -msgstr "oder" - -#: templates/account/login.html:19 -#, python-format -msgid "" -"If you have not created an account yet, then please\n" -"sign up first." -msgstr "" -"Wenn Sie noch kein Konto haben, Registrieren Sie " -"sich bitte erst." - -#: templates/account/login.html:29 msgid "Forgot Password?" msgstr "Passwort vergessen?" -#: templates/account/logout.html:4 templates/account/logout.html.py:5 +#: templates/account/logout.html:4 templates/account/logout.html:5 #: templates/account/logout.html:15 msgid "Sign Out" msgstr "Abmelden" @@ -317,22 +292,22 @@ msgstr "" msgid "Bad Token" msgstr "Ungültiges Token" -#: templates/account/password_reset_from_key.html:9 +#: templates/account/password_reset_from_key.html:10 #, python-format msgid "" "The password reset link was invalid, possibly because it has already been " -"used. Please request a new password resetnew password reset." msgstr "" "Der Passwortrücksetzungslink war ungültig, möglicherweise wurde er bereits " -"verwendet. Bitte fordern Sie erneut eine Passwortrücksetzung an." +"verwendet. Bitte fordern Sie erneut eine Passwortrücksetzung an." -#: templates/account/password_reset_from_key.html:15 +#: templates/account/password_reset_from_key.html:17 msgid "change password" msgstr "Passwort ändern" -#: templates/account/password_reset_from_key.html:18 +#: templates/account/password_reset_from_key.html:20 #: templates/account/password_reset_from_key_done.html:7 msgid "Your password is now changed." msgstr "Ihr Passwort wurde geändert." @@ -342,13 +317,12 @@ msgstr "Ihr Passwort wurde geändert." msgid "Set Password" msgstr "Passwort setzen" -#: templates/account/signup.html:4 templates/socialaccount/signup.html.py:4 +#: templates/account/signup.html:4 templates/socialaccount/signup.html:4 msgid "Signup" msgstr "Registrieren" -#: templates/account/signup.html:5 templates/account/signup.html.py:15 -#: templates/base.html:82 templates/socialaccount/signup.html.py:5 -#: templates/socialaccount/signup.html:15 +#: templates/account/signup.html:5 templates/account/signup.html:17 +#: templates/socialaccount/signup.html:5 templates/socialaccount/signup.html:18 msgid "Sign Up" msgstr "Registrieren" @@ -398,23 +372,23 @@ msgstr "" #: templates/account/verified_email_required.html:9 msgid "" -"This part of the site requires us to verify that\n" -"you are who you claim to be. For this purpose, we require that you\n" -"verify ownership of your e-mail address. " +"This part of the site requires us to verify that you are who you claim to " +"be. For this purpose, we require that you verify ownership of your e-mail " +"address." msgstr "" "Dieser Teil unseres Angebots erfordert eine Bestätigung, dass Sie derjenige " "sind, der Sie vorgeben zu sein. Zu diesem Zweck benötigen wir die " -"Bestätigung, dass Sie der Eigentümer Ihrer E-Mailadresse sind." +"Bestätigung, dass Sie der Eigentümer Ihrer E-Mail-Adresse sind." #: templates/account/verified_email_required.html:13 msgid "" -"We have sent an e-mail to you for\n" -"verification. Please click on the link inside this e-mail. Please\n" -"contact us if you do not receive it within a few minutes." +"We have sent an e-mail to you for verification. Please click on the link " +"inside this e-mail. Please contact us if you do not receive it within a few " +"minutes." msgstr "" -"Wir haben Ihnen zur Bestätigung eine E-Mail geschickt.\n" -"Bitte klicken Sie auf den Link in dieser Mail. Bitte kontaktieren\n" -"Sie uns, wenn Sie die E-Mail nicht in den nächsten Minuten erhalten." +"Wir haben Ihnen zur Bestätigung eine E-Mail geschickt. Bitte klicken Sie auf " +"den Link in dieser Mail. Bitte kontaktieren Sie uns, wenn Sie die E-Mail " +"nicht in den nächsten Minuten erhalten." #: templates/account/verified_email_required.html:17 #, python-format @@ -425,31 +399,31 @@ msgstr "" "Hinweis: Sie können Ihre E-" "Mailadresse noch ändern." -#: templates/base.html:43 +#: templates/base.html:32 msgid "Dashboard" msgstr "Dashboard" -#: templates/base.html:49 templates/base.html.py:56 +#: templates/base.html:34 +msgid "Toggle navigation" +msgstr "Navigation umschalten" + +#: templates/base.html:42 templates/base.html:53 msgid "Hosting" msgstr "Hosting" -#: templates/base.html:51 -#: templates/hostingpackages/customerhostingpackage_list.html:5 -#: templates/hostingpackages/customerhostingpackage_list.html:13 +#: templates/base.html:45 msgid "Your hosting packages" msgstr "Ihre Hostingpakete" -#: templates/base.html:52 -#: templates/hostingpackages/customerhostingpackage_admin_list.html:3 -#: templates/hostingpackages/customerhostingpackage_admin_list.html:4 +#: templates/base.html:47 msgid "All hosting packages" msgstr "Alle Hostingpakete" -#: templates/base.html:59 +#: templates/base.html:57 msgid "Links" msgstr "Links" -#: templates/base.html:61 +#: templates/base.html:60 msgid "Web based mail system" msgstr "Webbasiertes E-Mailsystem" @@ -457,512 +431,154 @@ msgstr "Webbasiertes E-Mailsystem" msgid "Webmail" msgstr "Webmail" -#: templates/base.html:62 +#: templates/base.html:63 msgid "phpMyAdmin - MySQL database administration tool" msgstr "phpMyAdmin - MySQL-Datenbankverwaltungswerkzeug" -#: templates/base.html:62 +#: templates/base.html:64 msgid "phpMyAdmin" msgstr "phpMyAdmin" -#: templates/base.html:63 +#: templates/base.html:66 msgid "phpPgAdmin - PostgreSQL database administration tool" msgstr "phpPgAdmin - PostgreSQL-Datenbankverwaltungswerkzeug" -#: templates/base.html:63 +#: templates/base.html:67 msgid "phpPgAdmin" msgstr "phpPgAdmin" -#: templates/base.html:66 +#: templates/base.html:71 msgid "Imprint" msgstr "Impressum" -#: templates/base.html:67 templates/contact_form/contact_form.html.py:4 -#: templates/contact_form/contact_form.html:5 -#: templates/contact_form/contact_success.html:4 -#: templates/contact_form/contact_success.html:5 +#: templates/base.html:74 +msgid "Privacy policy" +msgstr "Datenschutz" + +#: templates/base.html:78 msgid "Contact" msgstr "Kontakt" -#: templates/base.html:72 -msgid "My Account" -msgstr "Mein Konto" +#: templates/base.html:86 +#, python-format +msgid "" +"Signed in as %(user_display)s " +msgstr "" +"Angemeldet als %(user_display)s " -#: templates/base.html:74 -msgid "Admin site" -msgstr "Adminsite" - -#: templates/base.html:75 -msgid "Change Email" -msgstr "E-Mail ändern" - -#: templates/base.html:76 -msgid "Social Accounts" -msgstr "Konten in sozialen Netzwerken" - -#: templates/base.html:77 -msgid "Logout" -msgstr "Abmelden" - -#: templates/base.html:88 +#: templates/base.html:93 #, python-format msgid "" "Signed in as %(user_display)s" msgstr "" -"Angemeldet als %(user_display)s" +"Angemeldet als %(user_display)s" -#: templates/base.html:101 +#: templates/base.html:103 +msgid "My Account" +msgstr "Mein Konto" + +#: templates/base.html:107 +msgid "Impersonate user" +msgstr "Als Nutzer agieren" + +#: templates/base.html:111 +msgid "Admin site" +msgstr "Adminsite" + +#: templates/base.html:113 +msgid "Change Email" +msgstr "E-Mail ändern" + +#: templates/base.html:116 +msgid "Social Accounts" +msgstr "Konten in sozialen Netzwerken" + +#: templates/base.html:118 +msgid "Logout" +msgstr "Abmelden" + +#: templates/base.html:139 msgid "Close" msgstr "Schließen" -#: templates/contact_form/contact_success.html:8 -msgid "Your message has been sent successfully." -msgstr "Ihre Nachricht wurde erfolgreich übermittelt." - -#: templates/dashboard/index.html:3 -msgid "Welcome" -msgstr "Willkommen" - -#: templates/dashboard/index.html:4 -msgid "Welcome to our customer self service" -msgstr "Willkommen in unserem Selbstservice-System" - -#: templates/dashboard/index.html:7 -#, python-format -msgid "" -"Hello %(full_name)s,
\n" -"You can visit your Dashboard to view and " -"modify your hosting options.\n" -msgstr "" -"Hallo %(full_name)s,
\n" -"Sie können Ihre Startseite besuchen, um " -"Ihre Hostingeinstellungen anzusehen und zu bearbeiten.\n" - -#: templates/dashboard/index.html:11 -msgid "This is your entry to our customer self service sytem." -msgstr "Dies ist Ihr Einstieg in unser Selbstservice-System" - -#: templates/dashboard/index.html:12 -#, python-format -msgid "" -"If you are already a customer you can Sign in to view and modify your hosting options." -msgstr "" -"Wenn Sie bereits Kunde bei uns sind, können Sie sich Anmelden um Ihre Hostingeinstellungen anzusehen " -"und zu bearbeiten." - -#: templates/dashboard/user_dashboard.html:3 -#: templates/dashboard/user_dashboard.html:4 -#, python-format -msgid "Dashboard for %(full_name)s" -msgstr "Startseite für %(full_name)s" - -#: templates/dashboard/user_dashboard.html:9 -msgid "Hosting packages" -msgstr "Hostingpakete" - -#: templates/dashboard/user_dashboard.html:15 -#: templates/hostingpackages/customerhostingpackage_admin_list.html:11 -#: templates/hostingpackages/customerhostingpackage_detail.html:27 -#: templates/hostingpackages/customerhostingpackage_list.html:24 -msgid "Name" -msgstr "Name" - -#: templates/dashboard/user_dashboard.html:16 -#: templates/hostingpackages/customerhostingpackage_detail.html:31 -msgid "Disk space" -msgstr "Speicherplatz" - -#: templates/dashboard/user_dashboard.html:17 -#: templates/hostingpackages/customerhostingpackage_detail.html:38 -msgid "Mailboxes" -msgstr "Postfächer" - -#: templates/dashboard/user_dashboard.html:18 -#: templates/hostingpackages/customerhostingpackage_detail.html:177 -msgid "Databases" -msgstr "Datenbanken" - -#: templates/dashboard/user_dashboard.html:19 -#: templates/hostingpackages/customerhostingpackage_detail.html:87 -#: templates/hostingpackages/customerhostingpackage_detail.html:150 -#: templates/hostingpackages/customerhostingpackage_detail.html:185 -#: templates/osusers/sshpublickey_list.html:27 -msgid "Actions" -msgstr "Aktionen" - -#: templates/dashboard/user_dashboard.html:25 -#, python-format -msgid "Show details for %(packagename)s" -msgstr "Details für %(packagename)s anzeigen" - -#: templates/dashboard/user_dashboard.html:28 -#, python-format -msgid "" -"The reserved disk space for your hosting package is %(diskspace)s bytes." -msgstr "" -"Der für Ihr Hostingpaket reservierte Speicherplatz sind %(diskspace)s Bytes." - -#: templates/dashboard/user_dashboard.html:31 -#, python-format -msgid "used %(num)s of %(total)s" -msgstr "%(num)s von %(total)s genutzt" - -#: templates/dashboard/user_dashboard.html:42 -msgid "You have no hosting packages yet." -msgstr "Sie haben noch keine Hostingpakete." - -#: templates/dashboard/user_dashboard.html:42 -msgid "This user has no hosting packages assigned yet." -msgstr "Diesem Benutzer sind noch keine Hostingpakete zugewiesen." - -#: templates/dashboard/user_dashboard.html:45 -#: templates/hostingpackages/customerhostingpackage_admin_list.html:30 -#: templates/hostingpackages/customerhostingpackage_list.html:41 -msgid "Add hosting package" -msgstr "Hostingpaket anlegen" - -#: templates/domains/hostingdomain_create.html:3 #: templates/domains/hostingdomain_create.html:4 +#: templates/domains/hostingdomain_create.html:7 #, python-format msgid "Add Domain to Hosting Package %(package)s of Customer %(full_name)s" msgstr "" "Domain zum Hostingpaket %(package)s des Kunden %(full_name)s hinzufügen" -#: templates/hostingpackages/add_hosting_option.html:3 -#: templates/hostingpackages/add_hosting_option.html:4 +#: templates/impersonate/list_users.html:4 +msgid "Django Impersonate - User List" +msgstr "Django Impersonate - Nutzerliste" + +#: templates/impersonate/list_users.html:5 #, 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" +msgid "User List - Page %(page_number)s" +msgstr "Nutzerliste - Seite %(page_number)s" -#: templates/hostingpackages/customerhostingpackage_admin_list.html:12 -msgid "Customer" -msgstr "Kunde" +#: templates/impersonate/list_users.html:21 +msgid "Search users" +msgstr "Nutzer suchen" -#: templates/hostingpackages/customerhostingpackage_admin_list.html:13 -#: templates/hostingpackages/customerhostingpackage_list.html:25 -msgid "Setup date" -msgstr "Einrichtungsdatum" +#: templates/impersonate/search_users.html:4 +msgid "Django Impersonate - Search Users" +msgstr "Django Impersonate - Nutzersuche" -#: templates/hostingpackages/customerhostingpackage_admin_list.html:27 -msgid "There are no hosting packages setup yet." -msgstr "Es sind noch keine Hostingpakete eingerichtet." +#: templates/impersonate/search_users.html:11 +msgid "Enter Search Query:" +msgstr "Suchanfrage eingeben:" -#: 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" +#: templates/impersonate/search_users.html:13 +msgid "user name part" +msgstr "Nutzernamen-Bestandteile" -#: templates/hostingpackages/customerhostingpackage_create.html:4 -#, python-format -msgid "Add Hosting Package for Customer %(full_name)s" -msgstr "Hosting Paket für Kunde %(full_name)s" +#: templates/impersonate/search_users.html:15 +msgid "Search" +msgstr "Suchen" -#: templates/hostingpackages/customerhostingpackage_detail.html:6 -#, python-format -msgid "Details for your Hosting Package %(package)s" -msgstr "Details zu Ihrem Hostingpaket %(package)s" - -#: templates/hostingpackages/customerhostingpackage_detail.html:8 -#, python-format -msgid "Details for Hosting Package %(package)s of %(full_name)s" -msgstr "Details zum Hostingpaket %(package)s von %(full_name)s" - -#: templates/hostingpackages/customerhostingpackage_detail.html:12 -#, python-format -msgid "Details of Hosting Package %(package)s" -msgstr "Details zum Hostingpaket %(package)s" - -#: templates/hostingpackages/customerhostingpackage_detail.html:19 -msgid "Hosting Package Information" -msgstr "Informationen zum Hostingpaket" - -#: templates/hostingpackages/customerhostingpackage_detail.html:22 -msgid "Edit Hosting Package Information" -msgstr "Informationen zum Hostingpaket ändern" - -#: templates/hostingpackages/customerhostingpackage_detail.html:29 -msgid "Description" -msgstr "Beschreibung" - -#: templates/hostingpackages/customerhostingpackage_detail.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 beträgt %(diskspace)s " -"Bytes." - -#: templates/hostingpackages/customerhostingpackage_detail.html:35 -#, 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" - -#: templates/hostingpackages/customerhostingpackage_detail.html:39 -#, python-format -msgid "%(num)s of %(total)s in use" -msgstr "%(num)s von %(total)s genutzt" - -#: templates/hostingpackages/customerhostingpackage_detail.html:40 -#, 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" - -#: templates/hostingpackages/customerhostingpackage_detail.html:41 -msgid "SFTP username" -msgstr "SFTP-Benutzername" - -#: templates/hostingpackages/customerhostingpackage_detail.html:41 -msgid "SSH/SFTP username" -msgstr "SSH/SFTP-Benutzername" - -#: templates/hostingpackages/customerhostingpackage_detail.html:42 -#, 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." - -#: templates/hostingpackages/customerhostingpackage_detail.html:43 -msgid "Upload server" -msgstr "Uploadserver" - -#: templates/hostingpackages/customerhostingpackage_detail.html:50 -msgid "Hosting Package Options" -msgstr "Hostingpaketoptionen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:58 -msgid "No options booked" -msgstr "Keine Optionen gebucht" - -#: templates/hostingpackages/customerhostingpackage_detail.html:61 -msgid "Add another hosting option" -msgstr "Eine weitere Hostingoption hinzufügen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:61 -msgid "Add option" -msgstr "Option hinzufügen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:67 -msgid "Hosting Package Actions" -msgstr "Aktionen zum Hostingpaket" - -#: templates/hostingpackages/customerhostingpackage_detail.html:69 -msgid "Edit Hosting Package Description" -msgstr "Beschreibung des Hostingpakets bearbeiten" - -#: templates/hostingpackages/customerhostingpackage_detail.html:69 -msgid "Edit description" -msgstr "Beschreibung bearbeiten" - -#: templates/hostingpackages/customerhostingpackage_detail.html:70 -msgid "Set SFTP password" -msgstr "SFTP-Passwort setzen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:70 -msgid "Set SSH/SFTP password" -msgstr "SSH/SFTP-Passwort setzen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:71 -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" - -#: templates/hostingpackages/customerhostingpackage_detail.html:71 -#: templates/osusers/sshpublickey_list.html:46 -msgid "Add SSH public key" -msgstr "SSH-Schlüssel hinzufügen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:79 -msgid "Domains" -msgstr "Domains" - -#: templates/hostingpackages/customerhostingpackage_detail.html:84 -msgid "Domain name" -msgstr "Domainname" - -#: templates/hostingpackages/customerhostingpackage_detail.html:85 -#: templates/hostingpackages/customerhostingpackage_detail.html:148 -msgid "Mail addresses" -msgstr "E-Mailadressen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:86 -msgid "Websites" -msgstr "Webauftritte" - -#: templates/hostingpackages/customerhostingpackage_detail.html:87 -msgid "Domain actions" -msgstr "Domainaktionen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:98 -msgid "Edit mail address targets" -msgstr "E-Mailadressziele bearbeiten" - -#: templates/hostingpackages/customerhostingpackage_detail.html:99 -msgid "Delete mail address" -msgstr "E-Mailadresse löschen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:104 -#: templates/hostingpackages/customerhostingpackage_detail.html:116 -msgid "None" -msgstr "Keine" - -#: templates/hostingpackages/customerhostingpackage_detail.html:111 -msgid "Delete website" -msgstr "Webauftritt löschen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:120 -msgid "Add mail address" -msgstr "E-Mailadresse hinzufügen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:123 -msgid "Add website" -msgstr "Webauftritt anlegen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:131 -msgid "There are no domains assigned to this hosting package yet." -msgstr "Diesem Paket sind noch keine Domains zugeordnet." - -#: templates/hostingpackages/customerhostingpackage_detail.html:134 -msgid "Add domain" -msgstr "Domain hinzufügen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:142 -msgid "E-Mail-Accounts" -msgstr "E-Mailkonten" - -#: templates/hostingpackages/customerhostingpackage_detail.html:147 -msgid "Mailbox" -msgstr "Postfach" - -#: templates/hostingpackages/customerhostingpackage_detail.html:149 -#: templates/hostingpackages/customerhostingpackage_detail.html:158 -msgid "Active" -msgstr "Aktiv" - -#: templates/hostingpackages/customerhostingpackage_detail.html:150 -msgid "Mailbox actions" -msgstr "Postfachaktionen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:158 -msgid "inactive" -msgstr "inaktiv" - -#: templates/hostingpackages/customerhostingpackage_detail.html:160 -msgid "Set mailbox password" -msgstr "Postfachpasswort setzen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:166 -msgid "There are no mailboxes assigned to this hosting package yet." -msgstr "Diesem Hostingpaket sind noch keine Postfächer zugeordnet." - -#: templates/hostingpackages/customerhostingpackage_detail.html:169 -msgid "Add mailbox" -msgstr "Postfach hinzufügen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:182 -msgid "Database name" -msgstr "Datenbankname" - -#: templates/hostingpackages/customerhostingpackage_detail.html:183 -msgid "Database user" -msgstr "Datenbanknutzer" - -#: templates/hostingpackages/customerhostingpackage_detail.html:184 -msgid "Database type" -msgstr "Datenbanktyp" - -#: templates/hostingpackages/customerhostingpackage_detail.html:184 -msgid "Type" -msgstr "Typ" - -#: templates/hostingpackages/customerhostingpackage_detail.html:185 -msgid "Database actions" -msgstr "Datenbankaktionen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:195 -msgid "Set database user password" -msgstr "Datenbanknutzerpasswort setzen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:196 -msgid "Delete database" -msgstr "Datenbank löschen" - -#: templates/hostingpackages/customerhostingpackage_detail.html:203 -msgid "There are no databases assigned to this hosting package yet." -msgstr "Diesem Hostingpaket sind noch keine Datenbanken zugeordnet." - -#: templates/hostingpackages/customerhostingpackage_detail.html:206 -msgid "Add database" -msgstr "Datenbank hinzufügen" - -#: templates/hostingpackages/customerhostingpackage_list.html:7 -#, python-format -msgid "Hosting Packages of %(customer)s" -msgstr "Hostingpakete des Kunden %(customer)s" - -#: templates/hostingpackages/customerhostingpackage_list.html:15 -#, python-format -msgid "Hosting Packages of %(customer)s" -msgstr "Hostingpakete des Kunden %(customer)s" - -#: templates/hostingpackages/customerhostingpackage_list.html:38 -msgid "You have no hosting packages setup yet." -msgstr "Es wurden noch keine Hostingpakete für Sie eingerichtet." - -#: templates/hostingpackages/customerhostingpackage_list.html:38 -msgid "There are no hosting packages setup for this customer yet." -msgstr "Es wurden noch keine Hostingpakete für diesen Kunden eingerichtet." - -#: templates/hostingpackages/customerhostingpackage_option_choices.html:4 -#: templates/hostingpackages/customerhostingpackage_option_choices.html:6 -#, 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" +#: templates/impersonate/search_users.html:16 +msgid "List all users" +msgstr "Alle Nutzer auflisten" #: templates/managemails/mailaddress_confirm_delete.html:6 -#: templates/managemails/mailaddress_confirm_delete.html:14 +#: templates/managemails/mailaddress_confirm_delete.html:16 #, python-format msgid "Delete Mail Address %(mailaddress)s" msgstr "E-Mailadresse %(mailaddress)s löschen" #: templates/managemails/mailaddress_confirm_delete.html:8 -#: templates/managemails/mailaddress_confirm_delete.html:16 +#: templates/managemails/mailaddress_confirm_delete.html:18 #, python-format msgid "Delete Mail Address %(mailaddress)s of Customer %(full_name)s" msgstr "E-Mailadresse %(mailaddress)s des Kunden %(full_name)s löschen" -#: templates/managemails/mailaddress_confirm_delete.html:23 +#: templates/managemails/mailaddress_confirm_delete.html:26 #, python-format msgid "Do you really want to delete the mail address %(mailaddress)s?" msgstr "Wollen Sie die E-Mailadresse %(mailaddress)s wirklich löschen?" -#: templates/managemails/mailaddress_confirm_delete.html:28 -#: templates/osusers/sshpublickey_confirm_delete.html:30 -#: templates/userdbs/userdatabase_confirm_delete.html:29 -#: templates/websites/website_confirm_delete.html:29 +#: templates/managemails/mailaddress_confirm_delete.html:33 +#: templates/osusers/sshpublickey_confirm_delete.html:41 +#: templates/userdbs/userdatabase_confirm_delete.html:42 +#: templates/websites/website_confirm_delete.html:38 msgid "Yes, do it!" msgstr "Ja, so soll es sein!" -#: templates/managemails/mailaddress_confirm_delete.html:29 -#: templates/osusers/sshpublickey_confirm_delete.html:31 -#: templates/userdbs/userdatabase_confirm_delete.html:30 -#: templates/websites/website_confirm_delete.html:30 +#: templates/managemails/mailaddress_confirm_delete.html:34 +#: templates/osusers/sshpublickey_confirm_delete.html:43 +#: templates/userdbs/userdatabase_confirm_delete.html:43 +#: templates/websites/website_confirm_delete.html:39 msgid "Cancel" msgstr "Abbrechen" @@ -974,12 +590,8 @@ msgstr "Neue E-Mailadresse hinzufügen" #: templates/managemails/mailaddress_create.html:8 #: templates/managemails/mailaddress_create.html:18 #, python-format -msgid "" -"\n" -"Add new Mail Address for Customer %(full_name)s\n" -msgstr "" -"\n" -"Neue E-Mailadresse für Kunde %(full_name)s hinzufügen\n" +msgid "Add new Mail Address for Customer %(full_name)s" +msgstr "Neue E-Mailadresse für Kunde %(full_name)s hinzufügen" #: templates/managemails/mailaddress_edit.html:6 #: templates/managemails/mailaddress_edit.html:16 @@ -989,51 +601,47 @@ msgstr "E-Mailadressziel ändern" #: templates/managemails/mailaddress_edit.html:8 #: templates/managemails/mailaddress_edit.html:18 #, python-format -msgid "" -"\n" -"Change target of Mail Address for Customer %(full_name)s\n" -msgstr "" -"\n" -"E-Mailadressziel der E-Mailadresse des Kunden %(full_name)s ändern\n" +msgid "Change target of Mail Address for Customer %(full_name)s" +msgstr "E-Mailadressziel der E-Mailadresse des Kunden %(full_name)s ändern" #: templates/managemails/mailbox_create.html:6 -#: templates/managemails/mailbox_create.html:15 +#: templates/managemails/mailbox_create.html:17 #, python-format msgid "Add Mailbox to Hosting Package %(package)s" msgstr "Postfach zum Hostingpaket %(package)s hinzufügen" #: templates/managemails/mailbox_create.html:8 -#: templates/managemails/mailbox_create.html:17 +#: templates/managemails/mailbox_create.html:19 #, python-format msgid "Add Mailbox to Hosting Package %(package)s of Customer %(full_name)s" msgstr "" "Postfach zum Hostingpaket %(package)s des Kunden %(full_name)s hinzufügen" -#: templates/managemails/mailbox_create.html:23 +#: templates/managemails/mailbox_create.html:28 msgid "Please specify the password for your new mailbox." msgstr "Bitte geben Sie das Passwort für Ihr neues Postfach ein." -#: templates/managemails/mailbox_create.html:23 +#: templates/managemails/mailbox_create.html:30 msgid "Please specify the password for the new mailbox." msgstr "Bitte geben Sie das Passwort für das neue Postfach ein." #: templates/managemails/mailbox_setpassword.html:6 -#: templates/managemails/mailbox_setpassword.html:15 +#: templates/managemails/mailbox_setpassword.html:17 #, python-format msgid "Set Password for Mailbox %(mailbox)s" msgstr "Passwort für Postfach %(mailbox)s setzen" #: templates/managemails/mailbox_setpassword.html:8 -#: templates/managemails/mailbox_setpassword.html:17 +#: templates/managemails/mailbox_setpassword.html:19 #, python-format msgid "Set Password for Mailbox %(mailbox)s of Customer %(full_name)s" msgstr "Passwort für Postfach %(mailbox)s des Kunden %(full_name)s setzen" -#: templates/managemails/mailbox_setpassword.html:23 +#: templates/managemails/mailbox_setpassword.html:27 msgid "Please specify the new password for your mailbox." msgstr "Bitte geben Sie das neue Passwort für Ihr Postfach ein." -#: templates/managemails/mailbox_setpassword.html:23 +#: templates/managemails/mailbox_setpassword.html:28 msgid "Please specify the new password for the mailbox." msgstr "Bitte geben Sie das neue Passwort für das Postfach ein." @@ -1051,14 +659,14 @@ msgstr "" "SSH-Schlüssel des Betriebssystemnutzers %(osuser)s des Kunden %(full_name)s " "löschen" -#: templates/osusers/sshpublickey_confirm_delete.html:14 +#: templates/osusers/sshpublickey_confirm_delete.html:16 #, python-format msgid "" "Delete SSH Public Key for Operating System User %(osuser)s" msgstr "" "SSH-Schlüssel löschen für Betriebssystemnutzer %(osuser)s" -#: templates/osusers/sshpublickey_confirm_delete.html:16 +#: templates/osusers/sshpublickey_confirm_delete.html:20 #, python-format msgid "" "Delete SSH Public Key for Operating System User %(osuser)s of " @@ -1067,12 +675,12 @@ msgstr "" "SSH-Schlüssel löschen von Betriebssystemnutzer %(osuser)s des Kunden " "%(full_name)s" -#: templates/osusers/sshpublickey_confirm_delete.html:23 +#: templates/osusers/sshpublickey_confirm_delete.html:29 #, python-format msgid "Do you really want to delete the %(algorithm)s SSH public key?" msgstr "Wollen Sie den %(algorithm)s-SSH-Schlüssel wirklich löschen?" -#: templates/osusers/sshpublickey_confirm_delete.html:26 +#: templates/osusers/sshpublickey_confirm_delete.html:34 msgid "" "When you confirm the deletion of this key you will no longer be able to use " "the corresponding private key for authentication." @@ -1081,7 +689,7 @@ msgstr "" "dazugehörigen privaten Schlüssel nicht weiter für die Anmeldung verwenden " "können." -#: templates/osusers/sshpublickey_confirm_delete.html:31 +#: templates/osusers/sshpublickey_confirm_delete.html:43 msgid "Cancel and go back to the SSH key list" msgstr "Abbrechen und zurückgehen zur Liste der SSH-Schlüssel" @@ -1090,7 +698,7 @@ msgstr "Abbrechen und zurückgehen zur Liste der SSH-Schlüssel" msgid "Add new SSH Public Key for Operating System User %(osuser)s" msgstr "Neuen SSH-Schlüssel für Betriebssystemnutzer %(osuser)s hinterlegen" -#: templates/osusers/sshpublickey_create.html:8 +#: templates/osusers/sshpublickey_create.html:10 #, python-format msgid "" "Add a new SSH Public Key for Operating System User %(osuser)s of Customer " @@ -1099,7 +707,7 @@ msgstr "" "Neuen SSH-Schlüssel für Betriebssystemnutzer %(osuser)s des Kunden " "%(full_name)s hinterlegen" -#: templates/osusers/sshpublickey_create.html:14 +#: templates/osusers/sshpublickey_create.html:18 #, python-format msgid "" "Add new SSH Public Key for Operating System User %(osuser)s" @@ -1107,7 +715,7 @@ msgstr "" "Neuen SSH-Schlüssel hinterlegen für Betriebssystemnutzer %(osuser)s" -#: templates/osusers/sshpublickey_create.html:16 +#: templates/osusers/sshpublickey_create.html:22 #, python-format msgid "" "Add a new SSH Public Key for Operating System User %(osuser)s of " @@ -1131,7 +739,7 @@ msgstr "" "Kommentar des SSH-Schlüssels des Betriebssystemnutzers %(osuser)s des Kunden " "%(full_name)s ändern" -#: templates/osusers/sshpublickey_edit_comment.html:14 +#: templates/osusers/sshpublickey_edit_comment.html:16 #, python-format msgid "" "Edit Comment of Public Key for Operating System User %(osuser)sfür Betriebssystemnutzer " "%(osuser)s" -#: templates/osusers/sshpublickey_edit_comment.html:16 +#: templates/osusers/sshpublickey_edit_comment.html:20 #, python-format msgid "" "Edit Comment of SSH Public Key for Operating System User %(osuser)s " @@ -1162,12 +770,12 @@ msgid "" msgstr "" "SSH-Schlüssel des Betriebssystemnutzers %(osuser)s des Kunden %(full_name)s" -#: templates/osusers/sshpublickey_list.html:14 +#: templates/osusers/sshpublickey_list.html:16 #, python-format msgid "SSH Public Keys for Operating System User %(osuser)s" msgstr "SSH-Schlüssel für Betriebssystemnutzer %(osuser)s" -#: templates/osusers/sshpublickey_list.html:16 +#: templates/osusers/sshpublickey_list.html:20 #, python-format msgid "" "SSH Public Keys for Operating System User %(osuser)s of Customer " @@ -1176,50 +784,57 @@ msgstr "" "SSH-Schlüssel des Betriebssystemnutzers %(osuser)s des Kunden " "%(full_name)s" -#: templates/osusers/sshpublickey_list.html:25 +#: templates/osusers/sshpublickey_list.html:31 msgid "Algorithm" msgstr "Algorithmus" -#: templates/osusers/sshpublickey_list.html:26 +#: templates/osusers/sshpublickey_list.html:32 msgid "Comment" msgstr "Kommentar" -#: templates/osusers/sshpublickey_list.html:27 +#: templates/osusers/sshpublickey_list.html:33 msgid "SSH public key actions" msgstr "Aktionen für SSH-Schlüssel" -#: templates/osusers/sshpublickey_list.html:36 +#: templates/osusers/sshpublickey_list.html:34 +msgid "Actions" +msgstr "Aktionen" + +#: templates/osusers/sshpublickey_list.html:44 msgid "Delete this SSH public key" msgstr "Diesen SSH-Schlüssel löschen" -#: templates/osusers/sshpublickey_list.html:36 +#: templates/osusers/sshpublickey_list.html:46 msgid "Delete" msgstr "Löschen" -#: templates/osusers/sshpublickey_list.html:37 +#: templates/osusers/sshpublickey_list.html:48 msgid "Edit this SSH public key's comment" msgstr "Den Kommentar dieses SSH-Schlüssels ändern" -#: templates/osusers/sshpublickey_list.html:37 +#: templates/osusers/sshpublickey_list.html:50 msgid "Edit Comment" msgstr "Kommentar ändern" -#: templates/osusers/sshpublickey_list.html:44 +#: templates/osusers/sshpublickey_list.html:57 msgid "There are now SSH public keys set for this operating system user yet." msgstr "Diesem Betriebssytemnutzer wurden noch keine SSH-Schlüssel zugeordnet." +#: templates/osusers/sshpublickey_list.html:60 +msgid "Add SSH public key" +msgstr "SSH-Schlüssel hinzufügen" + #: templates/osusers/user_setpassword.html:5 -#: templates/osusers/user_setpassword.html:13 +#: templates/osusers/user_setpassword.html:15 #, python-format msgid "Set new password for user %(osuser)s" msgstr "Neues Passwort für Benutzer %(osuser)s setzen" #: templates/osusers/user_setpassword.html:7 -#: templates/osusers/user_setpassword.html:15 +#: templates/osusers/user_setpassword.html:17 #, python-format msgid "Set new password for user %(osuser)s of customer %(full_name)s" -msgstr "" -"Neues Passwort für Benutzer %(osuser)s des Kunden %(full_name)s setzen." +msgstr "Neues Passwort für Benutzer %(osuser)s des Kunden %(full_name)s setzen" #: templates/registration/login.html:3 templates/registration/login.html:10 msgid "Sign in" @@ -1247,32 +862,63 @@ msgid "" "You can sign in to your account using any of the following third party " "accounts:" msgstr "" -"Sie können Sich mit Ihrem Konto bei einem der folgenden Drittanbieter " -"anmelden:" +"You can sign in to your account using any of the following third party " +"accounts:" -#: templates/socialaccount/connections.html:18 +#: templates/socialaccount/connections.html:21 msgid "Select" msgstr "Auswählen" -#: templates/socialaccount/connections.html:19 +#: templates/socialaccount/connections.html:22 msgid "Provider" msgstr "Anbieter" -#: templates/socialaccount/connections.html:20 +#: templates/socialaccount/connections.html:23 msgid "Account name" msgstr "Kontoname" -#: templates/socialaccount/connections.html:39 +#: templates/socialaccount/connections.html:45 msgid "" "You currently have no social network accounts connected to this account." msgstr "" "Bisher sind noch keine Konten aus sozialen Netzwerken mit diesem " "Benutzerkonto verknüpft." -#: templates/socialaccount/connections.html:42 +#: templates/socialaccount/connections.html:48 msgid "Add a 3rd Party Account" msgstr "Drittanbieterkonto hinzufügen" +#: templates/socialaccount/login.html:5 +msgid "Social Network Sign In" +msgstr "Anmeldung über ein soziales Netzwerk" + +#: templates/socialaccount/login.html:9 +#, python-format +msgid "Connect %(provider)s" +msgstr "Verbinde %(provider)s" + +#: templates/socialaccount/login.html:11 +#, python-format +msgid "You are about to connect a new third party account from %(provider)s." +msgstr "" +"Sie sind dabei Ihr Benutzerkonto mit einem Benutzerkonto bei %(provider)s zu " +"verbinden." + +#: templates/socialaccount/login.html:13 +#, python-format +msgid "Sign In Via %(provider)s" +msgstr "Anmelden mit %(provider)s" + +#: templates/socialaccount/login.html:15 +#, python-format +msgid "You are about to sign in using a third party account from %(provider)s." +msgstr "" +"Sie sind dabei sich mit einem Benutzerkonto von %(provider)s anzumelden." + +#: templates/socialaccount/login.html:20 +msgid "Continue" +msgstr "Fortsetzen" + #: templates/socialaccount/login_cancelled.html:4 #: templates/socialaccount/login_cancelled.html:5 msgid "Login Cancelled" @@ -1282,8 +928,8 @@ msgstr "Anmeldung abgebrochen" #, python-format msgid "" "You decided to cancel logging in to our site using one of your existing " -"accounts. If this was a mistake, please proceed to sign in." +"accounts. If this was a mistake, please proceed to sign in." msgstr "" "Sie haben sich entschieden Ihre Anmeldung auf unserem Angebot mit einem " "Ihrer bereits bestehenden Konten abzubrechen. Wenn dies ein Versehen war, " @@ -1305,10 +951,10 @@ msgstr "Die Verknüpfung zum Drittanbieterkonto wurde entfernt." #: templates/socialaccount/signup.html:8 #, python-format msgid "" -"You are about to use your %(provider_name)s account to login to\n" +"You are about to use your %(provider_name)s account to login to " "%(site_name)s. As a final step, please complete the following form:" msgstr "" -"Sie sind im Begriff Ihr %(provider_name)s-Konto zur Anmeldung bei\n" +"Sie sind im Begriff Ihr %(provider_name)s-Konto zur Anmeldung bei " "%(site_name)s zu nutzen. Als letzten Schritt füllen Sie bitte folgendes " "Formular aus:" @@ -1321,14 +967,14 @@ msgstr "Neues Datenbanknutzerpasswort für %(dbuser)s setzen" #, python-format msgid "Set Database User Password for %(dbuser)s of Customer %(full_name)s" msgstr "" -"Neues Datenbanknutzerpasswort für %(dbuser)s des Kunden %(full_name)s setzen." +"Neues Datenbanknutzerpasswort für %(dbuser)s des Kunden %(full_name)s setzen" -#: templates/userdbs/databaseuser_setpassword.html:14 +#: templates/userdbs/databaseuser_setpassword.html:16 #, python-format msgid "Set Database User Password for %(dbuser)s" msgstr "Datenbanknutzerpasswort setzen für %(dbuser)s" -#: templates/userdbs/databaseuser_setpassword.html:16 +#: templates/userdbs/databaseuser_setpassword.html:20 #, python-format msgid "" "Set Database User Password for %(dbuser)s of Customer %(full_name)sfür %(dbuser)s des Kunden " "%(full_name)s" -#: templates/userdbs/databaseuser_setpassword.html:21 +#: templates/userdbs/databaseuser_setpassword.html:28 msgid "Please specify the new password for your database user." msgstr "Bitte geben Sie das neue Passwort für Ihren Datenbanknutzer ein." -#: templates/userdbs/databaseuser_setpassword.html:21 +#: templates/userdbs/databaseuser_setpassword.html:30 msgid "Please specify the new password of the database user." msgstr "Bitte geben Sie das neue Passwort für den Datenbanknutzer ein." @@ -1355,22 +1001,22 @@ msgstr "Datenbank %(database)s löschen" msgid "Delete Database %(database)s of customer %(full_name)s" msgstr "Datenbank %(database)s des Kunden %(full_name)s löschen" -#: templates/userdbs/userdatabase_confirm_delete.html:14 +#: templates/userdbs/userdatabase_confirm_delete.html:16 #, python-format msgid "Delete Database %(database)s" msgstr "Datenbank löschen %(database)s" -#: templates/userdbs/userdatabase_confirm_delete.html:16 +#: templates/userdbs/userdatabase_confirm_delete.html:20 #, python-format msgid "Delete Database %(database)s of customer %(full_name)s" msgstr "Datenbank löschen %(database)s des Kunden %(full_name)s" -#: templates/userdbs/userdatabase_confirm_delete.html:23 +#: templates/userdbs/userdatabase_confirm_delete.html:29 #, python-format msgid "Do you really want to delete the database %(database)s?" msgstr "Wollen Sie die Datenbank %(database)s wirklich löschen?" -#: templates/userdbs/userdatabase_confirm_delete.html:26 +#: templates/userdbs/userdatabase_confirm_delete.html:34 msgid "" "When you confirm the deletion the database will be removed from the database " "server. All data in the database will be lost! If the " @@ -1383,17 +1029,17 @@ msgstr "" "zugeordnet sind, wird er ebenfalls gelöscht." #: templates/userdbs/userdatabase_create.html:6 -#: templates/userdbs/userdatabase_create.html:14 +#: templates/userdbs/userdatabase_create.html:16 msgid "Add new Database" msgstr "Neue Datenbank hinzufügen" #: templates/userdbs/userdatabase_create.html:8 -#: templates/userdbs/userdatabase_create.html:16 +#: templates/userdbs/userdatabase_create.html:18 #, python-format msgid "Add new Database for Customer %(full_name)s" msgstr "Neue Datenbank für Kunde %(full_name)s anlegen" -#: templates/userdbs/userdatabase_create.html:21 +#: templates/userdbs/userdatabase_create.html:25 msgid "Please enter a password for a new database user for your database." msgstr "" "Bitte geben Sie ein Passwort für den neuen Datenbanknutzer für Ihre " @@ -1409,23 +1055,23 @@ msgstr "Webauftritt %(website)s löschen" msgid "Delete Website %(website)s of Customer %(full_name)s" msgstr "Webauftritt %(website)s des Kunden %(full_name)s löschen" -#: templates/websites/website_confirm_delete.html:14 +#: templates/websites/website_confirm_delete.html:16 #, python-format msgid "Delete Website %(website)s" msgstr "Webauftritt löschen %(website)s" -#: templates/websites/website_confirm_delete.html:16 +#: templates/websites/website_confirm_delete.html:18 #, python-format msgid "Delete Website %(website)s of Customer %(full_name)s" msgstr "" "Webauftritt löschen %(website)s des Kunden %(full_name)s" -#: templates/websites/website_confirm_delete.html:23 +#: templates/websites/website_confirm_delete.html:27 #, python-format msgid "Do you really want to delete the website %(website)s?" msgstr "Wollen Sie den Webauftritt %(website)s wirklich löschen?" -#: templates/websites/website_confirm_delete.html:26 +#: templates/websites/website_confirm_delete.html:30 msgid "" "Please be aware that the website directory is removed from the webserver and " "the webserver configuration is changed so that the website will not be " @@ -1450,12 +1096,12 @@ msgstr "" "Webauftritt für Subdomain der Domain %(domain)s des Kunden %(full_name)s " "anlegen" -#: templates/websites/website_create.html:14 +#: templates/websites/website_create.html:16 #, python-format msgid "Add Website for Subdomain of %(domain)s" msgstr "Website anlegen für Subdomain von %(domain)s" -#: templates/websites/website_create.html:16 +#: templates/websites/website_create.html:20 #, python-format msgid "" "Add Website for Subdomain of Domain %(domain)s of Customer " @@ -1463,11 +1109,3 @@ msgid "" msgstr "" "Webauftritt anlegen für Subdomain der Domain %(domain)s des Kunden " "%(full_name)s" - -#, fuzzy -#~| msgid "Password Reset" -#~ msgid "Password (again)" -#~ msgstr "Passwort zurücksetzen" - -#~ msgid "My Profile" -#~ msgstr "Mein Profil" diff --git a/gnuviechadmin/managemails/__init__.py b/gnuviechadmin/managemails/__init__.py index 75d9f0a..c16c60e 100644 --- a/gnuviechadmin/managemails/__init__.py +++ b/gnuviechadmin/managemails/__init__.py @@ -2,4 +2,3 @@ This app takes care of mailboxes and mail addresses. """ -default_app_config = 'managemails.apps.ManageMailsAppConfig' diff --git a/gnuviechadmin/managemails/admin.py b/gnuviechadmin/managemails/admin.py index 8bb386f..68a3c12 100644 --- a/gnuviechadmin/managemails/admin.py +++ b/gnuviechadmin/managemails/admin.py @@ -1,15 +1,10 @@ -from django.utils.html import format_html -from django.contrib import admin from django import forms +from django.contrib import admin from django.forms.utils import flatatt -from django.utils.translation import ugettext as _ +from django.utils.html import format_html +from django.utils.translation import gettext as _ -from .models import ( - MailAddress, - MailAddressForward, - MailAddressMailbox, - Mailbox, -) +from .models import MailAddress, MailAddressForward, MailAddressMailbox, Mailbox PASSWORD_MISMATCH_ERROR = _("Passwords don't match") @@ -17,8 +12,7 @@ PASSWORD_MISMATCH_ERROR = _("Passwords don't match") class ReadOnlyPasswordHashWidget(forms.Widget): def render(self, name, value, attrs): final_attrs = self.build_attrs(attrs) - summary = format_html("{0}: {1} ", - _('Hash'), value) + summary = format_html("{0}: {1} ", _("Hash"), value) return format_html("{1}", flatatt(final_attrs), summary) @@ -41,22 +35,21 @@ class MailboxCreationForm(forms.ModelForm): A form for creating mailboxes. """ - password1 = forms.CharField(label=_('Password'), - widget=forms.PasswordInput) - password2 = forms.CharField(label=_('Password (again)'), - widget=forms.PasswordInput) + + password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput) + password2 = forms.CharField(label=_("Password (again)"), widget=forms.PasswordInput) class Meta: model = Mailbox - fields = ('osuser',) + fields = ("osuser",) def clean_password2(self): """ Check that the two password entries match. """ - 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 @@ -67,9 +60,8 @@ class MailboxCreationForm(forms.ModelForm): """ mailbox = super(MailboxCreationForm, self).save(commit=False) - mailbox.username = Mailbox.objects.get_next_mailbox_name( - mailbox.osuser) - mailbox.set_password(self.cleaned_data['password1']) + mailbox.username = Mailbox.objects.get_next_mailbox_name(mailbox.osuser) + mailbox.set_password(self.cleaned_data["password1"]) if commit: mailbox.save() return mailbox @@ -80,14 +72,15 @@ class MailboxChangeForm(forms.ModelForm): A form for updating mailboxes. """ + password = ReadOnlyPasswordHashField() class Meta: model = Mailbox - fields = ('username', 'password', 'osuser', 'active') + fields = ("username", "password", "osuser", "active") def clean_password(self): - return self.initial['password'] + return self.initial["password"] class ActivationChangeMixin(object): @@ -97,8 +90,8 @@ class ActivationChangeMixin(object): def deactivate(self, request, queryset): queryset.update(active=False) - activate.short_description = _('Activate') - deactivate.short_description = _('Deactivate') + activate.short_description = _("Activate") + deactivate.short_description = _("Deactivate") class MailboxAdmin(ActivationChangeMixin, admin.ModelAdmin): @@ -106,24 +99,20 @@ class MailboxAdmin(ActivationChangeMixin, admin.ModelAdmin): Custom admin page for mailboxes. """ + form = MailboxChangeForm add_form = MailboxCreationForm - actions = ['activate', 'deactivate'] + actions = ["activate", "deactivate"] - list_display = ('username', 'osuser', 'active') - list_filter = ('active', 'osuser') - fieldsets = ( - (None, { - 'fields': ('osuser', 'username', 'password', 'active')}), - ) + list_display = ("username", "osuser", "active") + list_filter = ("active", "osuser") + fieldsets = ((None, {"fields": ("osuser", "username", "password", "active")}),) add_fieldsets = ( - (None, { - 'classes': ('wide',), - 'fields': ('osuser', 'password1', 'password2')}), + (None, {"classes": ("wide",), "fields": ("osuser", "password1", "password2")}), ) - search_fields = ('username',) - ordering = ('username',) + search_fields = ("username",) + ordering = ("username",) filter_horizontal = () def get_fieldsets(self, request, obj=None): @@ -138,10 +127,12 @@ class MailboxAdmin(ActivationChangeMixin, 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(MailboxAdmin, self).get_form(request, obj, **defaults) @@ -155,10 +146,10 @@ class MailAddressForwardInline(admin.TabularInline): class MailAddressAdmin(ActivationChangeMixin, admin.ModelAdmin): - actions = ['activate', 'deactivate'] + actions = ["activate", "deactivate"] - list_display = ('__str__', 'mailaddressmailbox', 'active') - list_filter = ('active', 'domain') + list_display = ("__str__", "mailaddressmailbox", "active") + list_filter = ("active", "domain") inlines = [MailAddressMailboxInline, MailAddressForwardInline] diff --git a/gnuviechadmin/managemails/apps.py b/gnuviechadmin/managemails/apps.py index 7a5067b..6ef7ce8 100644 --- a/gnuviechadmin/managemails/apps.py +++ b/gnuviechadmin/managemails/apps.py @@ -3,9 +3,8 @@ This module contains the :py:class:`django.apps.AppConfig` instance for the :py:mod:`managemails` app. """ -from __future__ import unicode_literals from django.apps import AppConfig -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ class ManageMailsAppConfig(AppConfig): @@ -13,5 +12,14 @@ class ManageMailsAppConfig(AppConfig): AppConfig for the :py:mod:`managemails` app. """ - name = 'managemails' - verbose_name = _('Mailboxes and Mail Addresses') + + name = "managemails" + verbose_name = _("Mailboxes and Mail Addresses") + + def ready(self): + """ + Takes care of importing the signal handlers of the :py:mod:`managemails` + app. + + """ + import managemails.signals # NOQA diff --git a/gnuviechadmin/managemails/forms.py b/gnuviechadmin/managemails/forms.py index dc1032a..27b6786 100644 --- a/gnuviechadmin/managemails/forms.py +++ b/gnuviechadmin/managemails/forms.py @@ -2,32 +2,24 @@ This module defines form classes for mailbox and mail address editing. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import +from crispy_forms.bootstrap import AppendedText +from crispy_forms.helper import FormHelper +from crispy_forms.layout import Div, Layout, Submit from django import forms from django.core.validators import validate_email from django.urls import reverse -from django.utils.translation import ugettext_lazy as _ - -from crispy_forms.helper import FormHelper -from crispy_forms.bootstrap import AppendedText -from crispy_forms.layout import ( - Div, - Layout, - Submit, -) - -from .models import ( - MailAddress, - Mailbox, -) -from gvawebcore.forms import PasswordModelFormMixin - +from django.utils.translation import gettext_lazy as _ from model_utils import Choices +from gvawebcore.forms import PasswordModelFormMixin + +from .models import MailAddress, Mailbox + MAILBOX_OR_FORWARDS = Choices( - (0, 'mailbox', _('Mailbox')), - (1, 'forwards', _('Forwards')), + (0, "mailbox", _("Mailbox")), + (1, "forwards", _("Forwards")), ) @@ -36,17 +28,19 @@ class CreateMailboxForm(PasswordModelFormMixin, forms.ModelForm): This form is used to create new Mailbox instances. """ + class Meta: model = Mailbox fields = [] def __init__(self, *args, **kwargs): - self.hosting_package = kwargs.pop('hostingpackage') + self.hosting_package = kwargs.pop("hostingpackage") super(CreateMailboxForm, self).__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_action = reverse( - 'create_mailbox', kwargs={'package': self.hosting_package.id}) - self.helper.add_input(Submit('submit', _('Create mailbox'))) + "create_mailbox", kwargs={"package": self.hosting_package.id} + ) + self.helper.add_input(Submit("submit", _("Create mailbox"))) def save(self, commit=True): """ @@ -60,7 +54,7 @@ class CreateMailboxForm(PasswordModelFormMixin, forms.ModelForm): osuser = self.hosting_package.osuser self.instance.osuser = osuser self.instance.username = Mailbox.objects.get_next_mailbox_name(osuser) - self.instance.set_password(self.cleaned_data['password1']) + self.instance.set_password(self.cleaned_data["password1"]) return super(CreateMailboxForm, self).save(commit=commit) @@ -69,20 +63,23 @@ class ChangeMailboxPasswordForm(PasswordModelFormMixin, forms.ModelForm): This form is used to set a new password for an existing mailbox. """ + class Meta: model = Mailbox fields = [] def __init__(self, *args, **kwargs): - self.hosting_package = kwargs.pop('hostingpackage') + self.hosting_package = kwargs.pop("hostingpackage") super(ChangeMailboxPasswordForm, self).__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_action = reverse( - 'change_mailbox_password', kwargs={ - 'package': self.hosting_package.id, - 'slug': self.instance.username, - }) - self.helper.add_input(Submit('submit', _('Set password'))) + "change_mailbox_password", + kwargs={ + "package": self.hosting_package.id, + "slug": self.instance.username, + }, + ) + self.helper.add_input(Submit("submit", _("Set password"))) def save(self, commit=True): """ @@ -93,13 +90,13 @@ class ChangeMailboxPasswordForm(PasswordModelFormMixin, forms.ModelForm): :rtype: :py:class:`managemails.models.Mailbox` """ - self.instance.set_password(self.cleaned_data['password1']) + self.instance.set_password(self.cleaned_data["password1"]) return super(ChangeMailboxPasswordForm, self).save(commit=commit) def multiple_email_validator(value): if value: - for email in [part.strip() for part in value.split(',')]: + for email in [part.strip() for part in value.split(",")]: validate_email(email) return value @@ -109,25 +106,26 @@ class MailAddressFieldMixin(forms.Form): This mixin defines form fields common to mail address forms. """ + mailbox_or_forwards = forms.TypedChoiceField( - label=_('Mailbox or Forwards'), + label=_("Mailbox or Forwards"), choices=MAILBOX_OR_FORWARDS, widget=forms.RadioSelect, coerce=int, ) mailbox = forms.ModelChoiceField( Mailbox.objects, - label=_('Mailbox'), + label=_("Mailbox"), required=False, ) # TODO: refactor as separate field class returning a list forwards = forms.CharField( - label=_('Forwards'), + label=_("Forwards"), required=False, error_messages={ - 'invalid': _( - 'Please enter one or more email addresses separated by ' - 'commas.'), + "invalid": _( + "Please enter one or more email addresses separated by " "commas." + ), }, validators=[multiple_email_validator], ) @@ -138,68 +136,71 @@ class AddMailAddressForm(forms.ModelForm, MailAddressFieldMixin): This form is used to add a new mail address. """ + class Meta: model = MailAddress - fields = ['localpart'] + fields = ["localpart"] def __init__(self, *args, **kwargs): - self.maildomain = kwargs.pop('maildomain') - self.hosting_package = kwargs.pop('hostingpackage') + self.maildomain = kwargs.pop("maildomain") + self.hosting_package = kwargs.pop("hostingpackage") super(AddMailAddressForm, self).__init__(*args, **kwargs) - self.fields['mailbox'].queryset = Mailbox.objects.unused( + self.fields["mailbox"].queryset = Mailbox.objects.unused( osuser=self.hosting_package.osuser, ) self.helper = FormHelper() self.helper.form_action = reverse( - 'add_mailaddress', kwargs={ - 'package': self.hosting_package.id, - 'domain': self.maildomain.domain, - }) + "add_mailaddress", + kwargs={ + "package": self.hosting_package.id, + "domain": self.maildomain.domain, + }, + ) self.helper.layout = Layout( Div( Div( - AppendedText('localpart', '@' + self.maildomain.domain), - css_class='col-lg-4 col-md-4 col-xs-12', + AppendedText("localpart", "@" + self.maildomain.domain), + css_class="col-lg-4 col-md-4 col-xs-12", ), Div( - 'mailbox_or_forwards', - css_class='col-lg-2 col-md-2 col-xs-12', + "mailbox_or_forwards", + css_class="col-lg-2 col-md-2 col-xs-12", ), Div( - 'mailbox', - 'forwards', - css_class='col-lg-6 col-md-6 col-xs-12', + "mailbox", + "forwards", + css_class="col-lg-6 col-md-6 col-xs-12", ), - css_class='row', + css_class="row", ), - Submit('submit', _('Add mail address')), + Submit("submit", _("Add mail address")), ) def clean_localpart(self): - localpart = self.cleaned_data['localpart'] + localpart = self.cleaned_data["localpart"] if MailAddress.objects.filter( domain=self.maildomain, localpart=localpart, ).exists(): - raise forms.ValidationError( - _('This mail address is already in use.')) - validate_email('{0}@{1}'.format(localpart, self.maildomain.domain)) + raise forms.ValidationError(_("This mail address is already in use.")) + validate_email("{0}@{1}".format(localpart, self.maildomain.domain)) return localpart def clean(self): super(AddMailAddressForm, self).clean() data = self.cleaned_data - if data['mailbox_or_forwards'] == MAILBOX_OR_FORWARDS.mailbox: - if not data['mailbox']: - self.add_error('mailbox', _('No mailbox selected')) - elif data['mailbox_or_forwards'] == MAILBOX_OR_FORWARDS.forwards: - if 'forwards' not in data or not data['forwards']: - self.add_error('forwards', _('No forward addresses selected')) + if data["mailbox_or_forwards"] == MAILBOX_OR_FORWARDS.mailbox: + if "mailbox" not in data or not data["mailbox"]: + self.add_error("mailbox", _("No mailbox selected")) + elif data["mailbox_or_forwards"] == MAILBOX_OR_FORWARDS.forwards: + if "forwards" not in data or not data["forwards"]: + self.add_error("forwards", _("No forward addresses selected")) else: # pragma: no cover # should not happen because of the field's validation raise forms.ValidationError( - _('Illegal choice for target of the mail address')) + _("Illegal choice for target of the mail address") + ) def save(self, commit=True): """ @@ -212,11 +213,11 @@ class AddMailAddressForm(forms.ModelForm, MailAddressFieldMixin): """ self.instance.domain = self.maildomain data = self.cleaned_data - target_choice = data['mailbox_or_forwards'] + target_choice = data["mailbox_or_forwards"] if target_choice == MAILBOX_OR_FORWARDS.mailbox: - mabox = self.instance.set_mailbox(data['mailbox'], commit=False) + mabox = self.instance.set_mailbox(data["mailbox"], commit=False) elif target_choice == MAILBOX_OR_FORWARDS.forwards: - targets = [part.strip() for part in data['forwards'].split(',')] + targets = [part.strip() for part in data["forwards"].split(",")] fwds = self.instance.set_forward_addresses(targets, commit=False) mailaddress = super(AddMailAddressForm, self).save(commit) if commit: @@ -235,53 +236,57 @@ class EditMailAddressForm(forms.ModelForm, MailAddressFieldMixin): This form is used to edit the targets for a mail address. """ + class Meta: model = MailAddress fields = [] def __init__(self, *args, **kwargs): - self.hosting_package = kwargs.pop('hostingpackage') - self.maildomain = kwargs.pop('maildomain') + self.hosting_package = kwargs.pop("hostingpackage") + self.maildomain = kwargs.pop("maildomain") super(EditMailAddressForm, self).__init__(*args, **kwargs) - self.fields['mailbox'].queryset = Mailbox.objects.unused_or_own( + self.fields["mailbox"].queryset = Mailbox.objects.unused_or_own( self.instance, self.hosting_package.osuser ) self.helper = FormHelper() self.helper.form_action = reverse( - 'edit_mailaddress', kwargs={ - 'package': self.hosting_package.id, - 'domain': self.maildomain.domain, - 'pk': self.instance.id, - }) + "edit_mailaddress", + kwargs={ + "package": self.hosting_package.id, + "domain": self.maildomain.domain, + "pk": self.instance.id, + }, + ) self.helper.layout = Layout( Div( Div( - 'mailbox_or_forwards', - css_class='col-log-2 col-md-2 col-xs-12', + "mailbox_or_forwards", + css_class="col-log-2 col-md-2 col-xs-12", ), Div( - 'mailbox', - 'forwards', - css_class='col-lg-10 col-md-10 col-xs-12', + "mailbox", + "forwards", + css_class="col-lg-10 col-md-10 col-xs-12", ), - css_class='row', + css_class="row", ), - Submit('submit', _('Change mail address targets')), + Submit("submit", _("Change mail address targets")), ) def clean(self): data = self.cleaned_data - if data['mailbox_or_forwards'] == MAILBOX_OR_FORWARDS.mailbox: - if not data['mailbox']: - self.add_error('mailbox', _('No mailbox selected')) - elif data['mailbox_or_forwards'] == MAILBOX_OR_FORWARDS.forwards: - if 'forwards' not in data or not data['forwards']: - self.add_error('forwards', _('No forward addresses selected')) + if data["mailbox_or_forwards"] == MAILBOX_OR_FORWARDS.mailbox: + if not data["mailbox"]: + self.add_error("mailbox", _("No mailbox selected")) + elif data["mailbox_or_forwards"] == MAILBOX_OR_FORWARDS.forwards: + if "forwards" not in data or not data["forwards"]: + self.add_error("forwards", _("No forward addresses selected")) else: # pragma: no cover # should not happen because of the field's validation raise forms.ValidationError( - _('Illegal choice for target of the mail address')) + _("Illegal choice for target of the mail address") + ) def save(self, commit=True): """ @@ -290,9 +295,9 @@ class EditMailAddressForm(forms.ModelForm, MailAddressFieldMixin): :param boolean commit: """ data = self.cleaned_data - if data['mailbox_or_forwards'] == MAILBOX_OR_FORWARDS.mailbox: - self.instance.set_mailbox(data['mailbox'], commit) - elif data['mailbox_or_forwards'] == MAILBOX_OR_FORWARDS.forwards: - targets = [part.strip() for part in data['forwards'].split(',')] + if data["mailbox_or_forwards"] == MAILBOX_OR_FORWARDS.mailbox: + self.instance.set_mailbox(data["mailbox"], commit) + elif data["mailbox_or_forwards"] == MAILBOX_OR_FORWARDS.forwards: + targets = [part.strip() for part in data["forwards"].split(",")] self.instance.set_forward_addresses(targets, commit) return super(EditMailAddressForm, self).save(commit) diff --git a/gnuviechadmin/managemails/locale/de/LC_MESSAGES/django.po b/gnuviechadmin/managemails/locale/de/LC_MESSAGES/django.po index 6e53cbc..9988858 100644 --- a/gnuviechadmin/managemails/locale/de/LC_MESSAGES/django.po +++ b/gnuviechadmin/managemails/locale/de/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: managemails\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-01-29 11:04+0100\n" -"PO-Revision-Date: 2015-01-25 22:17+0100\n" +"POT-Creation-Date: 2023-04-16 22:07+0200\n" +"PO-Revision-Date: 2023-04-16 18:21+0200\n" "Last-Translator: Jan Dittberner \n" "Language-Team: Jan Dittberner \n" "Language: de\n" @@ -16,30 +16,30 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.6.10\n" +"X-Generator: Poedit 3.2.2\n" "X-Poedit-SourceCharset: UTF-8\n" -#: managemails/admin.py:14 +#: managemails/admin.py:9 msgid "Passwords don't match" msgstr "Passwörter stimmen nicht überein" -#: managemails/admin.py:21 managemails/tests/test_admin.py:40 +#: managemails/admin.py:15 managemails/tests/test_admin.py:35 msgid "Hash" msgstr "Hash-Code" -#: managemails/admin.py:44 +#: managemails/admin.py:39 msgid "Password" msgstr "Passwort" -#: managemails/admin.py:46 +#: managemails/admin.py:40 msgid "Password (again)" msgstr "Passwortwiederholung" -#: managemails/admin.py:100 +#: managemails/admin.py:93 msgid "Activate" msgstr "Aktivieren" -#: managemails/admin.py:101 +#: managemails/admin.py:94 msgid "Deactivate" msgstr "Deaktivieren" @@ -47,32 +47,32 @@ msgstr "Deaktivieren" msgid "Mailboxes and Mail Addresses" msgstr "Postfächer und E-Mailadressen" -#: managemails/forms.py:29 managemails/forms.py:120 managemails/models.py:120 +#: managemails/forms.py:21 managemails/forms.py:118 managemails/models.py:113 msgid "Mailbox" msgstr "Postfach" -#: managemails/forms.py:30 managemails/forms.py:125 +#: managemails/forms.py:22 managemails/forms.py:123 msgid "Forwards" msgstr "Weiterleitungen" -#: managemails/forms.py:49 +#: managemails/forms.py:43 msgid "Create mailbox" msgstr "Postfach anlegen" -#: managemails/forms.py:85 +#: managemails/forms.py:82 msgid "Set password" msgstr "Passwort setzen" -#: managemails/forms.py:113 +#: managemails/forms.py:111 msgid "Mailbox or Forwards" msgstr "Postfach oder Weiterleitungen" -#: managemails/forms.py:129 +#: managemails/forms.py:127 msgid "Please enter one or more email addresses separated by commas." msgstr "" "Bitte geben Sie eine oder mehrere durch Kommata getrennte E-Mailadressen ein." -#: managemails/forms.py:176 +#: managemails/forms.py:177 msgid "Add mail address" msgstr "E-Mailadresse hinzufügen" @@ -80,60 +80,60 @@ msgstr "E-Mailadresse hinzufügen" msgid "This mail address is already in use." msgstr "Diese E-Mailadresse wird bereits verwendet." -#: managemails/forms.py:195 managemails/forms.py:277 +#: managemails/forms.py:195 managemails/forms.py:281 msgid "No mailbox selected" msgstr "Kein Postfach ausgewählt" -#: managemails/forms.py:198 managemails/forms.py:280 +#: managemails/forms.py:198 managemails/forms.py:284 msgid "No forward addresses selected" msgstr "Keine Weiterleitungsadressen ausgewählt" -#: managemails/forms.py:202 managemails/forms.py:284 +#: managemails/forms.py:202 managemails/forms.py:288 msgid "Illegal choice for target of the mail address" msgstr "Ungültige Auswahl für das Ziel der E-Mailadresse" -#: managemails/forms.py:270 +#: managemails/forms.py:274 msgid "Change mail address targets" msgstr "E-Mailadressziele ändern" -#: managemails/models.py:121 +#: managemails/models.py:114 msgid "Mailboxes" msgstr "Postfächer" -#: managemails/models.py:164 +#: managemails/models.py:145 msgid "local part" msgstr "Lokaler Teil" -#: managemails/models.py:165 +#: managemails/models.py:147 msgid "domain" msgstr "Domain" -#: managemails/models.py:170 +#: managemails/models.py:153 msgid "Mail address" msgstr "E-Mailadresse" -#: managemails/models.py:171 +#: managemails/models.py:154 msgid "Mail addresses" msgstr "E-Mailadressen" -#: managemails/models.py:253 +#: managemails/models.py:236 msgid "mailaddress" msgstr "E-Mailadresse" -#: managemails/models.py:254 +#: managemails/models.py:241 msgid "mailbox" msgstr "Postfach" -#: managemails/views.py:52 +#: managemails/views.py:46 msgid "You are not allowed to add more mailboxes to this hosting package" msgstr "Sie können keine weiteren Postfächer zu diesem Hostingpaket hinzufügen" -#: managemails/views.py:71 +#: managemails/views.py:67 #, python-brace-format msgid "Mailbox {mailbox} created successfully." msgstr "Postfach {mailbox} erfolgreich angelegt." -#: managemails/views.py:106 +#: managemails/views.py:103 #, python-brace-format msgid "Successfully set new password for mailbox {mailbox}." msgstr "" @@ -144,7 +144,7 @@ msgstr "" msgid "Successfully added mail address {mailaddress}" msgstr "E-Mailadresse {mailaddress} erfolgreich hinzugefügt" -#: managemails/views.py:223 +#: managemails/views.py:230 #, python-brace-format msgid "Successfully updated mail address {mailaddress} targets." msgstr "Ziele der E-Mailadresse {mailaddress} erfolgreich aktualisiert." diff --git a/gnuviechadmin/managemails/migrations/0001_initial.py b/gnuviechadmin/managemails/migrations/0001_initial.py index 7b7b71f..383071a 100644 --- a/gnuviechadmin/managemails/migrations/0001_initial.py +++ b/gnuviechadmin/managemails/migrations/0001_initial.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - import django.utils.timezone import model_utils.fields from django.db import migrations, models @@ -8,122 +6,185 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('domains', '0001_initial'), - ('osusers', '0001_initial'), + ("domains", "0001_initial"), + ("osusers", "0001_initial"), ] operations = [ migrations.CreateModel( - name='MailAddress', + name="MailAddress", 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)), - ('active', models.BooleanField(default=True)), - ('localpart', models.CharField(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, + ), + ), + ("active", models.BooleanField(default=True)), + ("localpart", models.CharField(max_length=128)), ], options={ - 'verbose_name': 'Mail address', - 'verbose_name_plural': 'Mail addresses', + "verbose_name": "Mail address", + "verbose_name_plural": "Mail addresses", }, bases=(models.Model,), ), migrations.CreateModel( - name='MailAddressForward', + name="MailAddressForward", 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)), - ('target', models.EmailField(max_length=254)), + ( + "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, + ), + ), + ("target", models.EmailField(max_length=254)), ], - options={ - }, + options={}, bases=(models.Model,), ), migrations.CreateModel( - name='MailAddressMailbox', + name="MailAddressMailbox", fields=[ - ('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)), - ('mailaddress', models.OneToOneField( - primary_key=True, serialize=False, - to='managemails.MailAddress', on_delete=models.CASCADE)), + ( + "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, + ), + ), + ( + "mailaddress", + models.OneToOneField( + primary_key=True, + serialize=False, + to="managemails.MailAddress", + on_delete=models.CASCADE, + ), + ), ], - options={ - }, + options={}, bases=(models.Model,), ), migrations.CreateModel( - name='Mailbox', + name="Mailbox", 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)), - ('active', models.BooleanField(default=True)), - ('username', models.CharField(unique=True, max_length=128)), - ('password', models.CharField(max_length=255)), - ('osuser', models.ForeignKey( - to='osusers.User', 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, + ), + ), + ("active", models.BooleanField(default=True)), + ("username", models.CharField(unique=True, max_length=128)), + ("password", models.CharField(max_length=255)), + ( + "osuser", + models.ForeignKey(to="osusers.User", on_delete=models.CASCADE), + ), ], options={ - 'verbose_name': 'Mailbox', - 'verbose_name_plural': 'Mailboxes', + "verbose_name": "Mailbox", + "verbose_name_plural": "Mailboxes", }, bases=(models.Model,), ), migrations.AddField( - model_name='mailaddressmailbox', - name='mailbox', - field=models.ForeignKey( - to='managemails.Mailbox', on_delete=models.CASCADE), + model_name="mailaddressmailbox", + name="mailbox", + field=models.ForeignKey(to="managemails.Mailbox", on_delete=models.CASCADE), preserve_default=True, ), migrations.AlterUniqueTogether( - name='mailaddressmailbox', - unique_together={('mailaddress', 'mailbox')}, + name="mailaddressmailbox", + unique_together={("mailaddress", "mailbox")}, ), migrations.AddField( - model_name='mailaddressforward', - name='mailaddress', + model_name="mailaddressforward", + name="mailaddress", field=models.ForeignKey( - to='managemails.MailAddress', on_delete=models.CASCADE), + to="managemails.MailAddress", on_delete=models.CASCADE + ), preserve_default=True, ), migrations.AlterUniqueTogether( - name='mailaddressforward', - unique_together={('mailaddress', 'target')}, + name="mailaddressforward", + unique_together={("mailaddress", "target")}, ), migrations.AddField( - model_name='mailaddress', - name='domain', - field=models.ForeignKey( - to='domains.MailDomain', on_delete=models.CASCADE), + model_name="mailaddress", + name="domain", + field=models.ForeignKey(to="domains.MailDomain", on_delete=models.CASCADE), preserve_default=True, ), migrations.AlterUniqueTogether( - name='mailaddress', - unique_together={('localpart', 'domain')}, + name="mailaddress", + unique_together={("localpart", "domain")}, ), ] diff --git a/gnuviechadmin/managemails/migrations/0002_auto_20150117_1238.py b/gnuviechadmin/managemails/migrations/0002_auto_20150117_1238.py index 5c6d070..a9ada6a 100644 --- a/gnuviechadmin/managemails/migrations/0002_auto_20150117_1238.py +++ b/gnuviechadmin/managemails/migrations/0002_auto_20150117_1238.py @@ -1,22 +1,27 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations +from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('managemails', '0001_initial'), + ("managemails", "0001_initial"), ] operations = [ migrations.AlterModelOptions( - name='mailaddress', - options={'ordering': ['domain', 'localpart'], 'verbose_name': 'Mail address', 'verbose_name_plural': 'Mail addresses'}, + name="mailaddress", + options={ + "ordering": ["domain", "localpart"], + "verbose_name": "Mail address", + "verbose_name_plural": "Mail addresses", + }, ), migrations.AlterModelOptions( - name='mailbox', - options={'ordering': ['osuser', 'username'], 'verbose_name': 'Mailbox', 'verbose_name_plural': 'Mailboxes'}, + name="mailbox", + options={ + "ordering": ["osuser", "username"], + "verbose_name": "Mailbox", + "verbose_name_plural": "Mailboxes", + }, ), ] diff --git a/gnuviechadmin/managemails/migrations/0003_auto_20150124_2029.py b/gnuviechadmin/managemails/migrations/0003_auto_20150124_2029.py index 7863dbb..6812429 100644 --- a/gnuviechadmin/managemails/migrations/0003_auto_20150124_2029.py +++ b/gnuviechadmin/managemails/migrations/0003_auto_20150124_2029.py @@ -1,29 +1,33 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('managemails', '0002_auto_20150117_1238'), + ("managemails", "0002_auto_20150117_1238"), ] operations = [ migrations.AlterField( - model_name='mailaddressmailbox', - name='mailaddress', + model_name="mailaddressmailbox", + name="mailaddress", field=models.OneToOneField( - primary_key=True, serialize=False, to='managemails.MailAddress', - verbose_name='mailaddress', on_delete=models.CASCADE), + primary_key=True, + serialize=False, + to="managemails.MailAddress", + verbose_name="mailaddress", + on_delete=models.CASCADE, + ), preserve_default=True, ), migrations.AlterField( - model_name='mailaddressmailbox', - name='mailbox', + model_name="mailaddressmailbox", + name="mailbox", field=models.ForeignKey( - verbose_name='mailbox', to='managemails.Mailbox', - on_delete=models.CASCADE), + verbose_name="mailbox", + to="managemails.Mailbox", + on_delete=models.CASCADE, + ), preserve_default=True, ), ] diff --git a/gnuviechadmin/managemails/migrations/0004_auto_20150125_1825.py b/gnuviechadmin/managemails/migrations/0004_auto_20150125_1825.py index d1afccf..97bd009 100644 --- a/gnuviechadmin/managemails/migrations/0004_auto_20150125_1825.py +++ b/gnuviechadmin/managemails/migrations/0004_auto_20150125_1825.py @@ -1,27 +1,25 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('managemails', '0003_auto_20150124_2029'), + ("managemails", "0003_auto_20150124_2029"), ] operations = [ migrations.AlterField( - model_name='mailaddress', - name='domain', + model_name="mailaddress", + name="domain", field=models.ForeignKey( - verbose_name='domain', to='domains.MailDomain', - on_delete=models.CASCADE), + verbose_name="domain", to="domains.MailDomain", on_delete=models.CASCADE + ), preserve_default=True, ), migrations.AlterField( - model_name='mailaddress', - name='localpart', - field=models.CharField(max_length=128, verbose_name='local part'), + model_name="mailaddress", + name="localpart", + field=models.CharField(max_length=128, verbose_name="local part"), preserve_default=True, ), ] diff --git a/gnuviechadmin/managemails/models.py b/gnuviechadmin/managemails/models.py index 5d30164..cef5cf4 100644 --- a/gnuviechadmin/managemails/models.py +++ b/gnuviechadmin/managemails/models.py @@ -2,29 +2,21 @@ This module defines the database models for mail handling. """ -from __future__ import unicode_literals - from django.db import models -from django.utils.encoding import python_2_unicode_compatible -from django.utils.translation import ugettext as _ - -from passlib.hash import sha512_crypt +from django.utils.translation import gettext as _ from model_utils.models import TimeStampedModel +from passlib.handlers.sha2_crypt import sha512_crypt from domains.models import MailDomain from osusers.models import User as OsUser -from fileservertasks.tasks import ( - create_file_mailbox, - delete_file_mailbox, -) - class ActivateAbleMixin(models.Model): """ Mixin for model classes that can be active or inactive. """ + active = models.BooleanField(default=True) class Meta: @@ -51,10 +43,8 @@ class MailboxManager(models.Manager): mailboxformat = "{0}p{1:02d}" mailboxname = mailboxformat.format(osuser.username, count) - for box in self.values('username').filter(osuser=osuser).order_by( - 'username' - ): - if box['username'] == mailboxname: + for box in self.values("username").filter(osuser=osuser).order_by("username"): + if box["username"] == mailboxname: count += 1 mailboxname = mailboxformat.format(osuser.username, count) else: @@ -69,9 +59,10 @@ class MailboxManager(models.Manager): """ return self.filter( - models.Q(mailaddressmailbox__isnull=True) | - models.Q(mailaddressmailbox__mailaddress=mailaddress), - active=True, osuser=osuser, + models.Q(mailaddressmailbox__isnull=True) + | models.Q(mailaddressmailbox__mailaddress=mailaddress), + active=True, + osuser=osuser, ) def unused(self, osuser): @@ -82,7 +73,8 @@ class MailboxManager(models.Manager): """ return self.filter( mailaddressmailbox__isnull=True, - active=True, osuser=osuser, + active=True, + osuser=osuser, ) def create_mailbox(self, osuser, password=None, commit=True): @@ -97,18 +89,19 @@ class MailboxManager(models.Manager): """ mailbox = self.create( - osuser=osuser, username=self.get_next_mailbox_name(osuser)) + osuser=osuser, username=self.get_next_mailbox_name(osuser) + ) if password is not None: mailbox.set_password(password) return mailbox -@python_2_unicode_compatible class Mailbox(ActivateAbleMixin, TimeStampedModel): """ This is the model class for a mailbox. """ + osuser = models.ForeignKey(OsUser, on_delete=models.CASCADE) username = models.CharField(max_length=128, unique=True) password = models.CharField(max_length=255) @@ -116,9 +109,9 @@ class Mailbox(ActivateAbleMixin, TimeStampedModel): objects = MailboxManager() class Meta: - ordering = ['osuser', 'username'] - verbose_name = _('Mailbox') - verbose_name_plural = _('Mailboxes') + ordering = ["osuser", "username"] + verbose_name = _("Mailbox") + verbose_name_plural = _("Mailboxes") def set_password(self, password): """ @@ -127,49 +120,38 @@ class Mailbox(ActivateAbleMixin, TimeStampedModel): :param str password: the clear text password """ - self.password = sha512_crypt.encrypt(password) - - def save(self, *args, **kwargs): - # TODO: refactor to use signals - create_file_mailbox.delay(self.osuser.username, self.username).get() - super(Mailbox, self).save(*args, **kwargs) - - def delete(self, *args, **kwargs): - # TODO: refactor to use signals - delete_file_mailbox.delay(self.osuser.username, self.username).get() - super(Mailbox, self).delete(*args, **kwargs) + self.password = sha512_crypt.hash(password) def get_mailaddresses(self): """ Get a list of mail addresses assigned to this mailbox. """ - addrs = [ - mbadr.mailaddress for mbadr in - self.mailaddressmailbox_set.all() - ] + addrs = [mbadr.mailaddress for mbadr in self.mailaddressmailbox_set.all()] return addrs + mailaddresses = property(get_mailaddresses) def __str__(self): return self.username -@python_2_unicode_compatible class MailAddress(ActivateAbleMixin, TimeStampedModel, models.Model): """ This is the model class for a mail address. """ - localpart = models.CharField(_('local part'), max_length=128) + + localpart = models.CharField(_("local part"), max_length=128) domain = models.ForeignKey( - MailDomain, verbose_name=_('domain'), on_delete=models.CASCADE) + MailDomain, verbose_name=_("domain"), on_delete=models.CASCADE + ) class Meta: - ordering = ['domain', 'localpart'] - unique_together = ('localpart', 'domain') - verbose_name = _('Mail address') - verbose_name_plural = _('Mail addresses') + ordering = ["domain", "localpart"] + unique_together = ("localpart", "domain") + verbose_name = _("Mail address") + verbose_name_plural = _("Mail addresses") def __str__(self): return "{0}@{1}".format(self.localpart, self.domain) @@ -220,8 +202,7 @@ class MailAddress(ActivateAbleMixin, TimeStampedModel, models.Model): if MailAddressMailbox.objects.filter(mailaddress=self).exists(): mabox = MailAddressMailbox.objects.get(mailaddress=self) mabox.delete() - forwards = MailAddressForward.objects.filter( - mailaddress=self).all() + forwards = MailAddressForward.objects.filter(mailaddress=self).all() for item in forwards: if item.target not in addresses: item.delete() @@ -244,20 +225,24 @@ class MailAddress(ActivateAbleMixin, TimeStampedModel, models.Model): return retval -@python_2_unicode_compatible class MailAddressMailbox(TimeStampedModel, models.Model): """ This is the model class to assign a mail address to a mailbox. """ + mailaddress = models.OneToOneField( - MailAddress, verbose_name=_('mailaddress'), primary_key=True, - on_delete=models.CASCADE) + MailAddress, + verbose_name=_("mailaddress"), + primary_key=True, + on_delete=models.CASCADE, + ) mailbox = models.ForeignKey( - Mailbox, verbose_name=_('mailbox'), on_delete=models.CASCADE) + Mailbox, verbose_name=_("mailbox"), on_delete=models.CASCADE + ) class Meta: - unique_together = ('mailaddress', 'mailbox') + unique_together = ("mailaddress", "mailbox") def __str__(self): return self.mailbox.username @@ -268,8 +253,9 @@ class MailAddressForward(TimeStampedModel, models.Model): This is a model class to map mail addresses to forwarding addresses. """ + mailaddress = models.ForeignKey(MailAddress, on_delete=models.CASCADE) target = models.EmailField(max_length=254) class Meta: - unique_together = ('mailaddress', 'target') + unique_together = ("mailaddress", "target") diff --git a/gnuviechadmin/managemails/signals.py b/gnuviechadmin/managemails/signals.py new file mode 100644 index 0000000..d30d5e9 --- /dev/null +++ b/gnuviechadmin/managemails/signals.py @@ -0,0 +1,62 @@ +""" +This module contains the signal handlers of the :py:mod:`managemails` app. + +The module starts Celery_ tasks. + +.. _Celery: https://www.celeryproject.org/ + +""" +import logging + +from django.db.models.signals import post_delete, post_save +from django.dispatch import receiver + +from fileservertasks.tasks import create_file_mailbox, delete_file_mailbox +from managemails.models import Mailbox +from taskresults.models import TaskResult + +_LOGGER = logging.getLogger(__name__) + + +@receiver(post_save, sender=Mailbox) +def handle_mailbox_created(sender, instance, created, **kwargs): + """ + Handles post creation actions on :py:class:`Mailbox ` instances. + + :param sender: sender of the signal + :param instance: Mailbox instance + :param created: whether the instance has just been created + + This signal handler starts a Celery_ task. + + """ + if created: + task_result = TaskResult.objects.create_task_result( + "handle_mailbox_created", + create_file_mailbox.s(instance.osuser.username, instance.username), + ) + _LOGGER.info( + "Mailbox creation has been requested in task %s", task_result.task_id + ) + _LOGGER.debug( + "mailbox %s has been %s", instance, created and "created" or "updated" + ) + + +@receiver(post_delete, sender=Mailbox) +def handle_mailbox_deleted(sender, instance, **kwargs): + """ + Handles cleanup actions to be done after deletion of a + :py:class:`Mailbox ` instance. + + :param sender: sender of the signal + :param instance: Mailbox instance + + This signal handler starts a Celery_ task. + + """ + task_result = TaskResult.objects.create_task_result( + "handle_mailbox_deleted", + delete_file_mailbox.s(instance.osuser.username, instance.username), + ) + _LOGGER.info("Mailbox deletion has been requested in task %s", task_result.task_id) diff --git a/gnuviechadmin/managemails/tests/test_admin.py b/gnuviechadmin/managemails/tests/test_admin.py index ddacf4b..332b363 100644 --- a/gnuviechadmin/managemails/tests/test_admin.py +++ b/gnuviechadmin/managemails/tests/test_admin.py @@ -1,27 +1,25 @@ +from unittest.mock import Mock + from django import forms +from django.contrib.admin import AdminSite +from django.contrib.auth import get_user_model from django.test import TestCase from django.test.utils import override_settings from django.urls import reverse from django.utils.html import format_html -from django.utils.translation import ugettext as _ - -from django.contrib.admin import AdminSite -from django.contrib.auth import get_user_model - -from unittest.mock import Mock - -from osusers.models import User +from django.utils.translation import gettext as _ from managemails.admin import ( + PASSWORD_MISMATCH_ERROR, ActivationChangeMixin, MailboxAdmin, MailboxChangeForm, MailboxCreationForm, - PASSWORD_MISMATCH_ERROR, ReadOnlyPasswordHashField, ReadOnlyPasswordHashWidget, ) from managemails.models import Mailbox +from osusers.models import User Customer = get_user_model() diff --git a/gnuviechadmin/managemails/tests/test_forms.py b/gnuviechadmin/managemails/tests/test_forms.py index be72a61..32fe366 100644 --- a/gnuviechadmin/managemails/tests/test_forms.py +++ b/gnuviechadmin/managemails/tests/test_forms.py @@ -2,35 +2,40 @@ This module provides tests for :py:mod:`managemails.forms`. """ -from unittest.mock import MagicMock, Mock, patch, ANY +from unittest.mock import MagicMock, Mock, patch from django.forms import ValidationError from django.test import TestCase from django.urls import reverse +from osusers.tests.testutils import create_test_user + +from domains.models import MailDomain from managemails.forms import ( + MAILBOX_OR_FORWARDS, AddMailAddressForm, ChangeMailboxPasswordForm, CreateMailboxForm, EditMailAddressForm, - MAILBOX_OR_FORWARDS, MailAddressFieldMixin, multiple_email_validator, ) +from managemails.models import MailAddress, Mailbox +from taskresults.tests.testutils import TestCaseWithCeleryTasks class CreateMailboxFormTest(TestCase): - def test_constructor_needs_hostingpackage(self): - instance = MagicMock() + def test_constructor_needs_hosting_package(self): + instance = Mailbox() with self.assertRaises(KeyError): CreateMailboxForm(instance) def test_constructor(self): - hostingpackage = Mock(id=42) + hosting_package = Mock(id=42) instance = MagicMock() - form = CreateMailboxForm(instance, hostingpackage=hostingpackage) + form = CreateMailboxForm(instance, hostingpackage=hosting_package) self.assertTrue(hasattr(form, "hosting_package")) - self.assertEqual(form.hosting_package, hostingpackage) + self.assertEqual(form.hosting_package, hosting_package) self.assertTrue(hasattr(form, "helper")) self.assertEqual( form.helper.form_action, reverse("create_mailbox", kwargs={"package": 42}) @@ -130,43 +135,47 @@ class MailAddressFieldMixinTest(TestCase): self.assertIn("forwards", form.fields) -class AddMailAddressFormTest(TestCase): - def setUp(self): - self.patcher1 = patch("managemails.forms.Mailbox.objects") - self.patcher2 = patch("managemails.forms.MailAddress.objects") - self.mailbox_objects = self.patcher1.start() - self.mailaddress_objects = self.patcher2.start() +def create_test_mailbox(os_user=None): + if os_user is None: + os_user = create_test_user() - def tearDown(self): - self.patcher2.stop() - self.patcher1.stop() + mail_box = Mailbox.objects.create(osuser=os_user, username="mailbox23") + mail_box.set_password("test") - def test_constructor_needs_hostingpackage(self): - instance = MagicMock() + return mail_box + + +def create_test_mail_domain(): + return MailDomain.objects.create(domain="example.org") + + +class AddMailAddressFormTest(TestCaseWithCeleryTasks): + def setUp(self) -> None: + super().setUp() + self.mail_domain = create_test_mail_domain() + self.instance = MailAddress() + self.os_user = create_test_user() + self.hosting_package = create_test_hosting_package(os_user=self.os_user) + + def test_constructor_needs_hosting_package(self): with self.assertRaises(KeyError): - AddMailAddressForm(instance=instance, maildomain=MagicMock()) + AddMailAddressForm(instance=self.instance, maildomain=MagicMock()) - def test_constructor_needs_maildomain(self): - instance = MagicMock() + def test_constructor_needs_mail_domain(self): with self.assertRaises(KeyError): - AddMailAddressForm(instance=instance, hostingpackage=MagicMock()) + AddMailAddressForm(instance=self.instance, hostingpackage=MagicMock()) def test_constructor(self): - instance = MagicMock() - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") form = AddMailAddressForm( - instance=instance, hostingpackage=hostingpackage, maildomain=maildomain + instance=self.instance, hostingpackage=self.hosting_package, maildomain=self.mail_domain ) - self.mailbox_objects.unused.assert_called_with(osuser=osuser) self.assertIn("mailbox_or_forwards", form.fields) self.assertIn("mailbox", form.fields) self.assertIn("forwards", form.fields) self.assertTrue(hasattr(form, "hosting_package")) - self.assertEqual(form.hosting_package, hostingpackage) + self.assertEqual(form.hosting_package, self.hosting_package) self.assertTrue(hasattr(form, "maildomain")) - self.assertEqual(form.maildomain, maildomain) + self.assertEqual(form.maildomain, self.mail_domain) self.assertTrue(hasattr(form, "helper")) self.assertEqual( form.helper.form_action, @@ -176,424 +185,292 @@ class AddMailAddressFormTest(TestCase): self.assertEqual(form.helper.layout[1].name, "submit") def test_clean_localpart_valid(self): - instance = MagicMock() - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") form = AddMailAddressForm( - instance=instance, - hostingpackage=hostingpackage, - maildomain=maildomain, + instance=self.instance, + hostingpackage=self.hosting_package, + maildomain=self.mail_domain, data={ "localpart": "test", "mailbox_or_forwards": MAILBOX_OR_FORWARDS.forwards, "forwards": "test2@example.org", }, ) - self.mailaddress_objects.filter( - domain=maildomain, localpart="test" - ).exists.return_value = False self.assertTrue(form.is_valid()) self.assertEqual("test", form.clean_localpart()) def test_clean_localpart_duplicate(self): - instance = MagicMock() - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") + MailAddress.objects.create(localpart="test", domain=self.mail_domain) + form = AddMailAddressForm( - instance=instance, - hostingpackage=hostingpackage, - maildomain=maildomain, + instance=self.instance, + hostingpackage=self.hosting_package, + maildomain=self.mail_domain, data={ "localpart": "test", "mailbox_or_forwards": MAILBOX_OR_FORWARDS.forwards, "forwards": "test2@example.org", }, ) - self.mailaddress_objects.filter( - domain=maildomain, localpart="test" - ).exists.return_value = True self.assertFalse(form.is_valid()) self.assertIn("localpart", form.errors) def test_clean_no_mailbox_choice(self): - instance = MagicMock() - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") form = AddMailAddressForm( - instance=instance, - hostingpackage=hostingpackage, - maildomain=maildomain, + instance=self.instance, + hostingpackage=self.hosting_package, + maildomain=self.mail_domain, data={ "localpart": "test", "mailbox_or_forwards": MAILBOX_OR_FORWARDS.mailbox, }, ) - self.mailaddress_objects.filter( - domain=maildomain, localpart="test" - ).exists.return_value = False self.assertFalse(form.is_valid()) self.assertIn("mailbox", form.errors) def test_clean_no_forward_address_choice(self): - instance = MagicMock() - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") form = AddMailAddressForm( - instance=instance, - hostingpackage=hostingpackage, - maildomain=maildomain, + instance=self.instance, + hostingpackage=self.hosting_package, + maildomain=self.mail_domain, data={ "localpart": "test", "mailbox_or_forwards": MAILBOX_OR_FORWARDS.forwards, }, ) - self.mailaddress_objects.filter( - domain=maildomain, localpart="test" - ).exists.return_value = False self.assertFalse(form.is_valid()) self.assertIn("forwards", form.errors) def test_save_with_forwards_no_commit(self): - instance = MagicMock() - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") form = AddMailAddressForm( - instance=instance, - hostingpackage=hostingpackage, - maildomain=maildomain, + instance=self.instance, + hostingpackage=self.hosting_package, + maildomain=self.mail_domain, data={ "localpart": "test", "mailbox_or_forwards": MAILBOX_OR_FORWARDS.forwards, "forwards": "test2@example.org,test3@example.org", }, ) - self.mailaddress_objects.filter( - domain=maildomain, localpart="test" - ).exists.return_value = False self.assertTrue(form.is_valid()) - address1 = MagicMock(mailaddress="test2@example.org") - address2 = MagicMock(mailaddress="test3@example.org") - instance.set_forward_addresses.return_value = [address1, address2] form.save(commit=False) - self.assertEqual(maildomain, instance.domain) - instance.set_forward_addresses.assert_called_with( - ["test2@example.org", "test3@example.org"], commit=False - ) - address1.save.assert_not_called() - address2.save.assert_not_called() - instance.save.assert_not_called() + self.assertEqual(self.mail_domain, self.instance.domain) def test_save_with_forwards_commit(self): - instance = MagicMock() - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") form = AddMailAddressForm( - instance=instance, - hostingpackage=hostingpackage, - maildomain=maildomain, + instance=self.instance, + hostingpackage=self.hosting_package, + maildomain=self.mail_domain, data={ "localpart": "test", "mailbox_or_forwards": MAILBOX_OR_FORWARDS.forwards, "forwards": "test2@example.org,test3@example.org", }, ) - self.mailaddress_objects.filter( - domain=maildomain, localpart="test" - ).exists.return_value = False self.assertTrue(form.is_valid()) - address1 = MagicMock(mailaddress="test2@example.org") - address2 = MagicMock(mailaddress="test3@example.org") - instance.set_forward_addresses.return_value = [address1, address2] form.save(commit=True) - self.assertEqual(maildomain, instance.domain) - instance.set_forward_addresses.assert_called_with( - ["test2@example.org", "test3@example.org"], commit=False + self.assertEqual(self.mail_domain, self.instance.domain) + forwards = list( + self.instance.mailaddressforward_set.values_list("target", flat=True).order_by( + "target" + ) ) - address1.save.assert_called_with() - address2.save.assert_called_with() - instance.save.assert_called_with() + self.assertEqual(len(forwards), 2) + self.assertEqual(forwards, ["test2@example.org", "test3@example.org"]) def test_save_with_mailbox_no_commit(self): - instance = MagicMock() - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") + mail_box = create_test_mailbox(os_user=self.os_user) + form = AddMailAddressForm( - instance=instance, - hostingpackage=hostingpackage, - maildomain=maildomain, + instance=self.instance, + hostingpackage=self.hosting_package, + maildomain=self.mail_domain, data={ "localpart": "test", "mailbox_or_forwards": MAILBOX_OR_FORWARDS.mailbox, - "mailbox": "mailbox23", + "mailbox": mail_box, }, ) - self.mailaddress_objects.filter( - domain=maildomain, localpart="test" - ).exists.return_value = False self.assertTrue(form.is_valid()) - mailbox = MagicMock(osuser=osuser, username="testuserp01") - instance.set_mailbox.return_value = mailbox form.save(commit=False) - self.assertEqual(maildomain, instance.domain) - instance.set_mailbox.assert_called_with(ANY, commit=False) - mailbox.save.assert_not_called() - instance.save.assert_not_called() + self.assertEqual(self.mail_domain, self.instance.domain) def test_save_with_mailbox_commit(self): - instance = MagicMock() - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") + mail_box = create_test_mailbox(os_user=self.os_user) + form = AddMailAddressForm( - instance=instance, - hostingpackage=hostingpackage, - maildomain=maildomain, + instance=self.instance, + hostingpackage=self.hosting_package, + maildomain=self.mail_domain, data={ "localpart": "test", "mailbox_or_forwards": MAILBOX_OR_FORWARDS.mailbox, - "mailbox": "mailbox23", + "mailbox": mail_box, }, ) - self.mailaddress_objects.filter( - domain=maildomain, localpart="test" - ).exists.return_value = False self.assertTrue(form.is_valid()) - mailbox = MagicMock(osuser=osuser, username="testuserp01") - instance.set_mailbox.return_value = mailbox form.save(commit=True) - self.assertEqual(maildomain, instance.domain) - instance.set_mailbox.assert_called_with(ANY, commit=False) - instance.set_mailbox.return_value.save.assert_called_with() - mailbox.save.assert_called_with() - instance.save.assert_called_with() + self.assertEqual(self.mail_domain, self.instance.domain) def test_save_with_other_choice(self): - instance = MagicMock() - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") + mail_box = create_test_mailbox(os_user=self.os_user) + form = AddMailAddressForm( - instance=instance, - hostingpackage=hostingpackage, - maildomain=maildomain, + instance=self.instance, + hostingpackage=self.hosting_package, + maildomain=self.mail_domain, data={ "localpart": "test", "mailbox_or_forwards": MAILBOX_OR_FORWARDS.mailbox, - "mailbox": "mailbox23", + "mailbox": mail_box, }, ) - self.mailaddress_objects.filter( - domain=maildomain, localpart="test" - ).exists.return_value = False self.assertTrue(form.is_valid()) form.cleaned_data["mailbox_or_forwards"] = -1 - address1 = MagicMock(mailaddress="test2@example.org") - address2 = MagicMock(mailaddress="test3@example.org") - instance.set_forward_addresses.return_value = [address1, address2] - mailbox = MagicMock(osuser=osuser, username="testuserp01") - instance.set_mailbox.return_value = mailbox form.save(commit=True) - instance.set_mailbox.assert_not_called() - instance.set_forward_addresses.assert_not_called() - address1.save.assert_not_called() - address2.save.assert_not_called() - mailbox.save.assert_not_called() - instance.save.assert_called_with() -class EditMailAddressFormTest(TestCase): - def setUp(self): - self.patcher1 = patch("managemails.forms.Mailbox.objects") - self.patcher2 = patch("managemails.forms.MailAddress.objects") - self.mailbox_objects = self.patcher1.start() - self.mailaddress_objects = self.patcher2.start() +def create_test_hosting_package(os_user=None): + if os_user is None: + os_user = create_test_user() - def tearDown(self): - self.patcher2.stop() - self.patcher1.stop() + return MagicMock(id=42, osuser=os_user) - def test_constructor_needs_hostingpackage(self): + +class EditMailAddressFormTest(TestCaseWithCeleryTasks): + def setUp(self) -> None: + super().setUp() + self.os_user = create_test_user() + self.hosting_package = create_test_hosting_package(os_user=self.os_user) + self.mail_domain = create_test_mail_domain() + self.instance = MailAddress.objects.create(localpart="test", domain=self.mail_domain) + + def test_constructor_needs_hosting_package(self): instance = MagicMock() with self.assertRaises(KeyError): EditMailAddressForm(instance=instance, maildomain=MagicMock()) - def test_constructor_needs_maildomain(self): + def test_constructor_needs_mail_domain(self): instance = MagicMock() with self.assertRaises(KeyError): EditMailAddressForm(instance=instance, hostingpackage=MagicMock()) def test_constructor(self): - instance = MagicMock(id=23) - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") form = EditMailAddressForm( - instance=instance, maildomain=maildomain, hostingpackage=hostingpackage + instance=self.instance, maildomain=self.mail_domain, hostingpackage=self.hosting_package ) - self.mailbox_objects.unused_or_own.assert_called_with(instance, osuser) self.assertIn("mailbox_or_forwards", form.fields) self.assertIn("mailbox", form.fields) self.assertIn("forwards", form.fields) self.assertTrue(hasattr(form, "hosting_package")) - self.assertEqual(form.hosting_package, hostingpackage) + self.assertEqual(form.hosting_package, self.hosting_package) self.assertTrue(hasattr(form, "maildomain")) - self.assertEqual(form.maildomain, maildomain) + self.assertEqual(form.maildomain, self.mail_domain) self.assertTrue(hasattr(form, "helper")) self.assertEqual( form.helper.form_action, reverse( "edit_mailaddress", - kwargs={"package": 42, "domain": "example.org", "pk": 23}, + kwargs={"package": 42, "domain": "example.org", "pk": self.instance.pk}, ), ) self.assertEqual(len(form.helper.layout), 2) self.assertEqual(form.helper.layout[1].name, "submit") def test_clean_no_mailbox_choice(self): - instance = MagicMock(id=23) - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") form = EditMailAddressForm( - instance=instance, - maildomain=maildomain, - hostingpackage=hostingpackage, + instance=self.instance, + maildomain=self.mail_domain, + hostingpackage=self.hosting_package, data={"mailbox_or_forwards": MAILBOX_OR_FORWARDS.mailbox}, ) self.assertFalse(form.is_valid()) self.assertIn("mailbox", form.errors) def test_clean_no_forward_address_choice(self): - instance = MagicMock(id=23) - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") form = EditMailAddressForm( - instance=instance, - maildomain=maildomain, - hostingpackage=hostingpackage, + instance=self.instance, + maildomain=self.mail_domain, + hostingpackage=self.hosting_package, data={"mailbox_or_forwards": MAILBOX_OR_FORWARDS.forwards}, ) self.assertFalse(form.is_valid()) self.assertIn("forwards", form.errors) def test_save_with_forwards_no_commit(self): - instance = MagicMock(id=23) - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") form = EditMailAddressForm( - instance=instance, - maildomain=maildomain, - hostingpackage=hostingpackage, + instance=self.instance, + maildomain=self.mail_domain, + hostingpackage=self.hosting_package, data={ "mailbox_or_forwards": MAILBOX_OR_FORWARDS.forwards, "forwards": "test2@example.org,test3@example.org", }, ) self.assertTrue(form.is_valid()) - address1 = MagicMock(mailaddress="test2@example.org") - address2 = MagicMock(mailaddress="test3@example.org") - instance.set_forward_addresses.return_value = [address1, address2] form.save(commit=False) - instance.set_forward_addresses.assert_called_with( - ["test2@example.org", "test3@example.org"], False - ) - address1.save.assert_not_called() - address2.save.assert_not_called() - instance.save.assert_not_called() def test_save_with_forwards_commit(self): - instance = MagicMock(id=23) - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") form = EditMailAddressForm( - instance=instance, - maildomain=maildomain, - hostingpackage=hostingpackage, + instance=self.instance, + maildomain=self.mail_domain, + hostingpackage=self.hosting_package, data={ "mailbox_or_forwards": MAILBOX_OR_FORWARDS.forwards, "forwards": "test2@example.org,test3@example.org", }, ) self.assertTrue(form.is_valid()) - address1 = MagicMock(mailaddress="test2@example.org") - address2 = MagicMock(mailaddress="test3@example.org") - instance.set_forward_addresses.return_value = [address1, address2] form.save(commit=True) - instance.set_forward_addresses.assert_called_with( - ["test2@example.org", "test3@example.org"], True - ) - instance.save.assert_called_with() def test_save_with_mailbox_no_commit(self): - instance = MagicMock(id=23) - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") + mail_box = create_test_mailbox(os_user=self.os_user) + form = EditMailAddressForm( - instance=instance, - maildomain=maildomain, - hostingpackage=hostingpackage, + instance=self.instance, + maildomain=self.mail_domain, + hostingpackage=self.hosting_package, data={ "mailbox_or_forwards": MAILBOX_OR_FORWARDS.mailbox, - "mailbox": "mailbox23", + "mailbox": mail_box, }, ) self.assertTrue(form.is_valid()) - mailbox = MagicMock(osuser=osuser, username="testuserp01") - instance.set_mailbox.return_value = mailbox form.save(commit=False) - instance.set_mailbox.assert_called_with(ANY, False) - mailbox.save.assert_not_called() - instance.save.assert_not_called() + + self.instance.refresh_from_db() + self.assertFalse(hasattr(self.instance, "mailaddressmailbox")) def test_save_with_mailbox_commit(self): - instance = MagicMock(id=23) - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") + mail_box = create_test_mailbox(os_user=self.os_user) + form = EditMailAddressForm( - instance=instance, - maildomain=maildomain, - hostingpackage=hostingpackage, + instance=self.instance, + maildomain=self.mail_domain, + hostingpackage=self.hosting_package, data={ "mailbox_or_forwards": MAILBOX_OR_FORWARDS.mailbox, - "mailbox": "mailbox23", + "mailbox": mail_box, }, ) self.assertTrue(form.is_valid()) - mailbox = MagicMock(osuser=osuser, username="testuserp01") - instance.set_mailbox.return_value = mailbox - self.mailbox_objects.unused_or_own.get.return_value = mailbox form.save(commit=True) - instance.set_mailbox.assert_called_with(ANY, True) - instance.save.assert_called_with() + + self.instance.refresh_from_db() + self.assertEqual(self.instance.mailaddressmailbox.mailbox, mail_box) def test_save_with_other_choice(self): - instance = MagicMock(id=23) - osuser = Mock(username="testuser") - hostingpackage = MagicMock(id=42, osuser=osuser) - maildomain = MagicMock(domain="example.org") + mail_box = create_test_mailbox(os_user=self.os_user) + form = EditMailAddressForm( - instance=instance, - maildomain=maildomain, - hostingpackage=hostingpackage, + instance=self.instance, + maildomain=self.mail_domain, + hostingpackage=self.hosting_package, data={ "mailbox_or_forwards": MAILBOX_OR_FORWARDS.mailbox, - "mailbox": "mailbox23", + "mailbox": mail_box, }, ) self.assertTrue(form.is_valid()) + form.cleaned_data["mailbox_or_forwards"] = -1 form.save(commit=True) - instance.set_mailbox.assert_not_called() - instance.save.assert_called_with() diff --git a/gnuviechadmin/managemails/tests/test_models.py b/gnuviechadmin/managemails/tests/test_models.py index 895a078..18562d3 100644 --- a/gnuviechadmin/managemails/tests/test_models.py +++ b/gnuviechadmin/managemails/tests/test_models.py @@ -1,26 +1,20 @@ """ This module contains tests for :py:mod:`managemails.models` """ -from unittest.mock import patch - +from django.contrib.auth import get_user_model from django.test import TestCase, TransactionTestCase from django.test.utils import override_settings -from django.contrib.auth import get_user_model - -from passlib.hash import sha512_crypt +from passlib.handlers.sha2_crypt import sha512_crypt from domains.models import MailDomain -from osusers.models import User - from managemails.models import MailAddress, Mailbox +from osusers.models import User +from taskresults.tests.testutils import TestCaseWithCeleryTasks Customer = get_user_model() -@override_settings( - CELERY_ALWAYS_EAGER=True, CELERY_CACHE_BACKEND="memory", BROKER_BACKEND="memory" -) -class MailboxTest(TestCase): +class MailboxTest(TestCaseWithCeleryTasks): def setUp(self): super(MailboxTest, self).setUp() self.customer = Customer.objects.create_user("test") @@ -37,22 +31,32 @@ class MailboxTest(TestCase): mb.set_password("test") self.assertEqual(str(mb), "test") - @patch("managemails.models.create_file_mailbox") - def test_save(self, create_file_mailbox_task): + def test_save(self): user = User.objects.create_user(self.customer) + + self.resetCeleryTasks() + mb = Mailbox.objects.create_mailbox(user) self.assertIsNotNone(mb.pk) - create_file_mailbox_task.delay.assert_called_with(user.username, mb.username) - @patch("managemails.models.delete_file_mailbox") - def test_delete(self, delete_file_mailbox_task): + self.assertCeleryTasksRun([ + (1, "handle_mailbox_created"), + ]) + + def test_delete(self): user = User.objects.create_user(self.customer) mb = Mailbox.objects.create_mailbox(user) + + self.resetCeleryTasks() + mb.delete() self.assertIsNone(mb.pk) - delete_file_mailbox_task.delay.assert_called_with(user.username, mb.username) - def test_get_mailaddresses(self): + self.assertCeleryTasksRun([ + (1, "handle_mailbox_deleted"), + ]) + + def test_get_mail_addresses(self): user = User.objects.create_user(self.customer) mb = Mailbox.objects.create_mailbox(user) md = MailDomain.objects.create(domain="example.org") @@ -63,10 +67,7 @@ class MailboxTest(TestCase): self.assertIn(address, mailaddresses) -@override_settings( - CELERY_ALWAYS_EAGER=True, CELERY_CACHE_BACKEND="memory", BROKER_BACKEND="memory" -) -class MailAddressTest(TransactionTestCase): +class MailAddressTest(TestCaseWithCeleryTasks): def test__str__(self): md = MailDomain.objects.create(domain="example.org") ma = MailAddress.objects.create(localpart="test", domain=md) @@ -216,10 +217,7 @@ class MailAddressTest(TransactionTestCase): self.assertEqual(mafwds[0].target, "test2@example.org") -@override_settings( - CELERY_ALWAYS_EAGER=True, CELERY_CACHE_BACKEND="memory", BROKER_BACKEND="memory" -) -class MailboxManagerTest(TransactionTestCase): +class MailboxManagerTest(TestCaseWithCeleryTasks): def setUp(self): super(MailboxManagerTest, self).setUp() self.customer = Customer.objects.create_user("test") @@ -251,7 +249,9 @@ class MailboxManagerTest(TransactionTestCase): address = MailAddress.objects.create(localpart="test", domain=md) mailboxes = [Mailbox.objects.create_mailbox(self.user) for _ in range(2)] assignable = Mailbox.objects.unused_or_own(address, self.user) - self.assertQuerysetEqual(assignable, [repr(mb) for mb in mailboxes]) + self.assertQuerysetEqual( + assignable, [repr(mb) for mb in mailboxes], transform=repr + ) def test_unused_or_own_assigned(self): md = MailDomain.objects.create(domain="example.org") @@ -259,7 +259,9 @@ class MailboxManagerTest(TransactionTestCase): mailboxes = [Mailbox.objects.create_mailbox(self.user) for _ in range(2)] address.set_mailbox(mailboxes[0]) assignable = Mailbox.objects.unused_or_own(address, self.user) - self.assertQuerysetEqual(assignable, [repr(mb) for mb in mailboxes]) + self.assertQuerysetEqual( + assignable, [repr(mb) for mb in mailboxes], transform=repr + ) def test_unused_or_own_assigned_other(self): md = MailDomain.objects.create(domain="example.org") @@ -268,7 +270,7 @@ class MailboxManagerTest(TransactionTestCase): mailboxes = [Mailbox.objects.create_mailbox(self.user) for _ in range(2)] address2.set_mailbox(mailboxes[0]) assignable = Mailbox.objects.unused_or_own(address, self.user) - self.assertQuerysetEqual(assignable, [repr(mailboxes[1])]) + self.assertQuerysetEqual(assignable, [repr(mailboxes[1])], transform=repr) def test_unused_fresh(self): mailboxes = Mailbox.objects.unused(self.user) @@ -277,7 +279,7 @@ class MailboxManagerTest(TransactionTestCase): def test_unused_unassigned(self): mailbox = Mailbox.objects.create_mailbox(self.user) mailboxes = Mailbox.objects.unused(self.user) - self.assertQuerysetEqual(mailboxes, [repr(mailbox)]) + self.assertQuerysetEqual(mailboxes, [repr(mailbox)], transform=repr) def test_unused_assigned(self): md = MailDomain.objects.create(domain="example.org") @@ -285,7 +287,7 @@ class MailboxManagerTest(TransactionTestCase): mailboxes = [Mailbox.objects.create_mailbox(self.user) for _ in range(2)] address.set_mailbox(mailboxes[0]) assignable = Mailbox.objects.unused(self.user) - self.assertQuerysetEqual(assignable, [repr(mailboxes[1])]) + self.assertQuerysetEqual(assignable, [repr(mailboxes[1])], transform=repr) def test_create_mailbox_no_password(self): mailbox = Mailbox.objects.create_mailbox(self.user) @@ -300,10 +302,7 @@ class MailboxManagerTest(TransactionTestCase): self.assertTrue(sha512_crypt.verify("test", mailbox.password)) -@override_settings( - CELERY_ALWAYS_EAGER=True, CELERY_CACHE_BACKEND="memory", BROKER_BACKEND="memory" -) -class MailAddressMailboxTest(TestCase): +class MailAddressMailboxTest(TestCaseWithCeleryTasks): def setUp(self): super(MailAddressMailboxTest, self).setUp() self.customer = Customer.objects.create_user("test") diff --git a/gnuviechadmin/managemails/urls.py b/gnuviechadmin/managemails/urls.py index fcd4ef7..9beef77 100644 --- a/gnuviechadmin/managemails/urls.py +++ b/gnuviechadmin/managemails/urls.py @@ -3,9 +3,9 @@ This module defines the URL patterns for mailbox and mail address related views. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import -from django.conf.urls import url +from django.urls import re_path from .views import ( AddMailAddress, @@ -16,16 +16,29 @@ from .views import ( ) urlpatterns = [ - url(r'^(?P\d+)/mailbox/create$', - CreateMailbox.as_view(), name='create_mailbox'), - url(r'^(?P\d+)/mailbox/(?P[\w0-9]+)/setpassword$', - ChangeMailboxPassword.as_view(), name='change_mailbox_password'), - url(r'^(?P\d+)/mailaddress/(?P[\w0-9-.]+)/create$', - AddMailAddress.as_view(), name='add_mailaddress'), - url(r'^(?P\d+)/mailaddress/(?P[\w0-9-.]+)/(?P\d+)' - r'/edit$', - EditMailAddress.as_view(), name='edit_mailaddress'), - url(r'^(?P\d+)/mailaddress/(?P[\w0-9-.]+)/(?P\d+)' - r'/delete$', - DeleteMailAddress.as_view(), name='delete_mailaddress'), + re_path( + r"^(?P\d+)/mailbox/create$", + CreateMailbox.as_view(), + name="create_mailbox", + ), + re_path( + r"^(?P\d+)/mailbox/(?P[\w0-9]+)/setpassword$", + ChangeMailboxPassword.as_view(), + name="change_mailbox_password", + ), + re_path( + r"^(?P\d+)/mailaddress/(?P[\w0-9-.]+)/create$", + AddMailAddress.as_view(), + name="add_mailaddress", + ), + re_path( + r"^(?P\d+)/mailaddress/(?P[\w0-9-.]+)/(?P\d+)" r"/edit$", + EditMailAddress.as_view(), + name="edit_mailaddress", + ), + re_path( + r"^(?P\d+)/mailaddress/(?P[\w0-9-.]+)/(?P\d+)" r"/delete$", + DeleteMailAddress.as_view(), + name="delete_mailaddress", + ), ] diff --git a/gnuviechadmin/managemails/views.py b/gnuviechadmin/managemails/views.py index 80a9337..408b052 100644 --- a/gnuviechadmin/managemails/views.py +++ b/gnuviechadmin/managemails/views.py @@ -2,75 +2,71 @@ This module defines views for mailbox and mail address handling. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import +from django.contrib import messages from django.http import HttpResponseForbidden from django.shortcuts import get_object_or_404, redirect -from django.utils.translation import ugettext as _ -from django.views.generic.edit import ( - CreateView, - DeleteView, - UpdateView, -) -from django.contrib import messages - +from django.utils.translation import gettext as _ +from django.views.generic.edit import CreateView, DeleteView, UpdateView from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin -from gvawebcore.views import HostingPackageAndCustomerMixin from domains.models import MailDomain +from gvawebcore.views import HostingPackageAndCustomerMixin + from .forms import ( + MAILBOX_OR_FORWARDS, AddMailAddressForm, ChangeMailboxPasswordForm, CreateMailboxForm, EditMailAddressForm, - MAILBOX_OR_FORWARDS, -) -from .models import ( - MailAddress, - MailAddressMailbox, - Mailbox, ) +from .models import MailAddress, MailAddressMailbox, Mailbox class CreateMailbox( HostingPackageAndCustomerMixin, StaffOrSelfLoginRequiredMixin, CreateView ): """ - This view is used to setup new mailboxes for a customer hosting package. + This view is used to set up new mailboxes for a customer hosting package. """ + model = Mailbox - context_object_name = 'mailbox' - template_name_suffix = '_create' + context_object_name = "mailbox" + template_name_suffix = "_create" form_class = CreateMailboxForm def dispatch(self, request, *args, **kwargs): resp = super(CreateMailbox, self).dispatch(request, *args, **kwargs) - if request.method != 'POST': + if request.method != "POST": if not self.get_hosting_package().may_add_mailbox(): resp = HttpResponseForbidden( - _('You are not allowed to add more mailboxes to this' - ' hosting package')) + _( + "You are not allowed to add more mailboxes to this" + " hosting package" + ) + ) return resp def get_context_data(self, **kwargs): context = super(CreateMailbox, self).get_context_data(**kwargs) - context['hostingpackage'] = self.get_hosting_package() - context['customer'] = self.get_customer_object() + context["hostingpackage"] = self.get_hosting_package() + context["customer"] = self.get_customer_object() return context def get_form_kwargs(self): kwargs = super(CreateMailbox, self).get_form_kwargs() - kwargs['hostingpackage'] = self.get_hosting_package() + kwargs["hostingpackage"] = self.get_hosting_package() return kwargs def form_valid(self, form): mailbox = form.save() messages.success( self.request, - _('Mailbox {mailbox} created successfully.').format( + _("Mailbox {mailbox} created successfully.").format( mailbox=mailbox.username - ) + ), ) return redirect(self.get_hosting_package()) @@ -82,30 +78,31 @@ class ChangeMailboxPassword( This view is used to set a new password for an existing mailbox. """ - context_object_name = 'mailbox' + + context_object_name = "mailbox" form_class = ChangeMailboxPasswordForm model = Mailbox - slug_field = 'username' - template_name_suffix = '_setpassword' + slug_field = "username" + template_name_suffix = "_setpassword" def get_context_data(self, **kwargs): context = super(ChangeMailboxPassword, self).get_context_data(**kwargs) - context['hostingpackage'] = self.get_hosting_package() - context['customer'] = self.get_customer_object() + context["hostingpackage"] = self.get_hosting_package() + context["customer"] = self.get_customer_object() return context def get_form_kwargs(self): kwargs = super(ChangeMailboxPassword, self).get_form_kwargs() - kwargs['hostingpackage'] = self.get_hosting_package() + kwargs["hostingpackage"] = self.get_hosting_package() return kwargs def form_valid(self, form): mailbox = form.save() messages.success( self.request, - _('Successfully set new password for mailbox {mailbox}.').format( + _("Successfully set new password for mailbox {mailbox}.").format( mailbox=mailbox.username - ) + ), ) return redirect(self.get_hosting_package()) @@ -117,33 +114,37 @@ class AddMailAddress( This view is used to add a new mail address to a domain. """ - context_object_name = 'mailaddress' + + context_object_name = "mailaddress" form_class = AddMailAddressForm model = MailAddress - template_name_suffix = '_create' + template_name_suffix = "_create" def get_context_data(self, **kwargs): context = super(AddMailAddress, self).get_context_data(**kwargs) - context['customer'] = self.get_customer_object() + context["customer"] = self.get_customer_object() return context def get_maildomain(self): - return get_object_or_404(MailDomain, domain=self.kwargs['domain']) + return get_object_or_404(MailDomain, domain=self.kwargs["domain"]) def get_form_kwargs(self): kwargs = super(AddMailAddress, self).get_form_kwargs() - kwargs.update({ - 'hostingpackage': self.get_hosting_package(), - 'maildomain': self.get_maildomain(), - }) + kwargs.update( + { + "hostingpackage": self.get_hosting_package(), + "maildomain": self.get_maildomain(), + } + ) return kwargs def form_valid(self, form): address = form.save() messages.success( self.request, - _('Successfully added mail address {mailaddress}').format( - mailaddress=address) + _("Successfully added mail address {mailaddress}").format( + mailaddress=address + ), ) return redirect(self.get_hosting_package()) @@ -155,19 +156,22 @@ class DeleteMailAddress( This view is used to delete a mail address. """ - context_object_name = 'mailaddress' + + context_object_name = "mailaddress" model = MailAddress def get_maildomain(self): - return get_object_or_404(MailDomain, domain=self.kwargs['domain']) + return get_object_or_404(MailDomain, domain=self.kwargs["domain"]) def get_context_data(self, **kwargs): context = super(DeleteMailAddress, self).get_context_data(**kwargs) - context.update({ - 'customer': self.get_customer_object(), - 'hostingpackage': self.get_hosting_package(), - 'maildomain': self.get_maildomain(), - }) + context.update( + { + "customer": self.get_customer_object(), + "hostingpackage": self.get_hosting_package(), + "maildomain": self.get_maildomain(), + } + ) return context def get_success_url(self): @@ -182,45 +186,49 @@ class EditMailAddress( addresses. """ - context_object_name = 'mailaddress' + + context_object_name = "mailaddress" form_class = EditMailAddressForm model = MailAddress - template_name_suffix = '_edit' + template_name_suffix = "_edit" def get_maildomain(self): - return get_object_or_404(MailDomain, domain=self.kwargs['domain']) + return get_object_or_404(MailDomain, domain=self.kwargs["domain"]) def get_context_data(self, **kwargs): context = super(EditMailAddress, self).get_context_data(**kwargs) - context['customer'] = self.get_customer_object() + context["customer"] = self.get_customer_object() return context def get_form_kwargs(self): kwargs = super(EditMailAddress, self).get_form_kwargs() - kwargs.update({ - 'hostingpackage': self.get_hosting_package(), - 'maildomain': self.get_maildomain(), - }) + kwargs.update( + { + "hostingpackage": self.get_hosting_package(), + "maildomain": self.get_maildomain(), + } + ) return kwargs def get_initial(self): initial = super(EditMailAddress, self).get_initial() mailaddress = self.get_object() if MailAddressMailbox.objects.filter(mailaddress=mailaddress).exists(): - initial['mailbox'] = mailaddress.mailaddressmailbox.mailbox - initial['mailbox_or_forwards'] = MAILBOX_OR_FORWARDS.mailbox + initial["mailbox"] = mailaddress.mailaddressmailbox.mailbox + initial["mailbox_or_forwards"] = MAILBOX_OR_FORWARDS.mailbox elif mailaddress.mailaddressforward_set.exists(): - initial['forwards'] = ", ".join( + initial["forwards"] = ", ".join( fwd.target for fwd in mailaddress.mailaddressforward_set.all() ) - initial['mailbox_or_forwards'] = MAILBOX_OR_FORWARDS.forwards + initial["mailbox_or_forwards"] = MAILBOX_OR_FORWARDS.forwards return initial def form_valid(self, form): mailaddress = form.save() messages.success( self.request, - _('Successfully updated mail address {mailaddress} ' - 'targets.').format(mailaddress=mailaddress) + _("Successfully updated mail address {mailaddress} " "targets.").format( + mailaddress=mailaddress + ), ) return redirect(self.get_hosting_package()) diff --git a/gnuviechadmin/osusers/__init__.py b/gnuviechadmin/osusers/__init__.py index 24380a5..5452c01 100644 --- a/gnuviechadmin/osusers/__init__.py +++ b/gnuviechadmin/osusers/__init__.py @@ -2,4 +2,3 @@ This app is for managing operating system users and groups. """ -default_app_config = 'osusers.apps.OsusersAppConfig' diff --git a/gnuviechadmin/osusers/admin.py b/gnuviechadmin/osusers/admin.py index b6688f6..c32d166 100644 --- a/gnuviechadmin/osusers/admin.py +++ b/gnuviechadmin/osusers/admin.py @@ -8,11 +8,12 @@ The module starts Celery_ tasks. """ from django import forms from django.contrib import admin -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ from fileservertasks.tasks import set_file_ssh_authorized_keys from gvawebcore.forms import PASSWORD_MISMATCH_ERROR from taskresults.models import TaskResult + from .forms import DUPLICATE_SSH_PUBLIC_KEY_FOR_USER, INVALID_SSH_PUBLIC_KEY from .models import AdditionalGroup, Group, Shadow, SshPublicKey, User diff --git a/gnuviechadmin/osusers/apps.py b/gnuviechadmin/osusers/apps.py index 79276a1..67eeb50 100644 --- a/gnuviechadmin/osusers/apps.py +++ b/gnuviechadmin/osusers/apps.py @@ -3,10 +3,8 @@ This module contains the :py:class:`django.apps.AppConfig` instance for the :py:mod:`osusers` app. """ -from __future__ import unicode_literals - from django.apps import AppConfig -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ class OsusersAppConfig(AppConfig): @@ -14,8 +12,9 @@ class OsusersAppConfig(AppConfig): AppConfig for the :py:mod:`osusers` app. """ - name = 'osusers' - verbose_name = _('Operating System Users and Groups') + + name = "osusers" + verbose_name = _("Operating System Users and Groups") def ready(self): """ diff --git a/gnuviechadmin/osusers/forms.py b/gnuviechadmin/osusers/forms.py index 6ca67c5..d128d0a 100644 --- a/gnuviechadmin/osusers/forms.py +++ b/gnuviechadmin/osusers/forms.py @@ -2,14 +2,11 @@ This module defines operating system user related forms. """ -from __future__ import unicode_literals - -from django import forms -from django.urls import reverse -from django.utils.translation import ugettext_lazy as _ - from crispy_forms.helper import FormHelper from crispy_forms.layout import Submit +from django import forms +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ from gvawebcore.forms import PasswordModelFormMixin diff --git a/gnuviechadmin/osusers/locale/de/LC_MESSAGES/django.po b/gnuviechadmin/osusers/locale/de/LC_MESSAGES/django.po index 81dba23..ffd0efd 100644 --- a/gnuviechadmin/osusers/locale/de/LC_MESSAGES/django.po +++ b/gnuviechadmin/osusers/locale/de/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: osusers\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-01-29 11:04+0100\n" -"PO-Revision-Date: 2016-01-29 11:07+0100\n" +"POT-Creation-Date: 2023-04-16 22:07+0200\n" +"PO-Revision-Date: 2023-04-16 18:21+0200\n" "Last-Translator: Jan Dittberner \n" "Language-Team: Jan Dittberner \n" "Language: de\n" @@ -16,144 +16,144 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.8.6\n" +"X-Generator: Poedit 3.2.2\n" "X-Poedit-SourceCharset: UTF-8\n" -#: osusers/admin.py:53 +#: osusers/admin.py:49 msgid "Password" msgstr "Passwort" -#: osusers/admin.py:57 +#: osusers/admin.py:52 msgid "Password (again)" msgstr "Passwortwiederholung" -#: osusers/admin.py:166 +#: osusers/admin.py:167 msgid "Delete selected users" msgstr "Ausgewählte Nutzer löschen" -#: osusers/admin.py:207 +#: osusers/admin.py:210 msgid "Delete selected groups" msgstr "Ausgewählte Gruppen löschen" -#: osusers/admin.py:234 osusers/forms.py:62 +#: osusers/admin.py:238 osusers/forms.py:60 msgid "Key text" msgstr "Schlüsseltext" -#: osusers/admin.py:235 osusers/forms.py:63 +#: osusers/admin.py:240 osusers/forms.py:62 msgid "A SSH public key in either OpenSSH or RFC 4716 format" msgstr "" "Öffentlicher Teil eines SSH-Schlüssels entweder im OpenSSH- oder im RFC-4716-" "Format" -#: osusers/admin.py:361 +#: osusers/admin.py:389 msgid "Delete selected SSH public keys" msgstr "Ausgewählte SSH-Schlüssel löschen" -#: osusers/apps.py:18 +#: osusers/apps.py:17 msgid "Operating System Users and Groups" msgstr "Betriebssystemnutzer- und Gruppen" -#: osusers/forms.py:21 +#: osusers/forms.py:15 msgid "Invalid SSH public key data format." msgstr "Ungültiges Format für den öffentlichen Teil eines SSH-Schlüssels." -#: osusers/forms.py:23 +#: osusers/forms.py:17 msgid "This SSH public key is already assigned to this user." msgstr "Dieser SSH-Schlüssel wurde diesem Nutzer bereits zugeordnet." -#: osusers/forms.py:40 +#: osusers/forms.py:37 msgid "Set password" msgstr "Passwort setzen" -#: osusers/forms.py:76 +#: osusers/forms.py:77 msgid "Add SSH public key" msgstr "SSH-Schlüssel hinzufügen" -#: osusers/forms.py:135 +#: osusers/forms.py:139 msgid "Change Comment" msgstr "Kommentar ändern" -#: osusers/models.py:34 +#: osusers/models.py:26 msgid "You can not use a user's primary group." msgstr "Sie können nicht die primäre Gruppe des Nutzers verwenden." -#: osusers/models.py:64 +#: osusers/models.py:55 msgid "Group name" msgstr "Gruppenname" -#: osusers/models.py:66 +#: osusers/models.py:56 msgid "Group ID" msgstr "Gruppen-ID" -#: osusers/models.py:67 +#: osusers/models.py:57 msgid "Description" msgstr "Beschreibung" -#: osusers/models.py:69 +#: osusers/models.py:58 msgid "Group password" msgstr "Gruppenpasswort" -#: osusers/models.py:74 osusers/models.py:201 +#: osusers/models.py:63 osusers/models.py:190 msgid "Group" msgstr "Gruppe" -#: osusers/models.py:75 +#: osusers/models.py:64 msgid "Groups" msgstr "Gruppen" -#: osusers/models.py:198 +#: osusers/models.py:188 msgid "User name" msgstr "Nutzername" -#: osusers/models.py:200 +#: osusers/models.py:189 msgid "User ID" msgstr "Nutzer-ID" -#: osusers/models.py:202 +#: osusers/models.py:191 msgid "Gecos field" msgstr "GECOS-Feld" -#: osusers/models.py:203 +#: osusers/models.py:192 msgid "Home directory" msgstr "Home-Verzeichnis" -#: osusers/models.py:204 +#: osusers/models.py:193 msgid "Login shell" msgstr "Loginshell" -#: osusers/models.py:210 osusers/models.py:310 osusers/models.py:501 +#: osusers/models.py:199 osusers/models.py:303 osusers/models.py:513 msgid "User" msgstr "Nutzer" -#: osusers/models.py:211 +#: osusers/models.py:200 msgid "Users" msgstr "Nutzer" -#: osusers/models.py:311 +#: osusers/models.py:305 msgid "Encrypted password" msgstr "Verschlüsseltes Passwort" -#: osusers/models.py:313 +#: osusers/models.py:307 msgid "Date of last change" msgstr "Datum der letzten Änderung" -#: osusers/models.py:314 +#: osusers/models.py:308 msgid "This is expressed in days since Jan 1, 1970" msgstr "Ausgedrückt als Tage seit dem 1. Januar 1970" -#: osusers/models.py:317 +#: osusers/models.py:313 msgid "Minimum age" msgstr "Minimales Alter" -#: osusers/models.py:318 +#: osusers/models.py:314 msgid "Minimum number of days before the password can be changed" msgstr "Minmale Anzahl von Tagen bevor das Passwort geändert werden kann" -#: osusers/models.py:322 +#: osusers/models.py:319 msgid "Maximum age" msgstr "Maximales Alter" -#: osusers/models.py:323 +#: osusers/models.py:321 msgid "Maximum number of days after which the password has to be changed" msgstr "" "Maximale Anzahl von Tagen, nach denen das Passwort geändert werden muss" @@ -166,11 +166,11 @@ msgstr "Duldungsperiode" msgid "The number of days before the password is going to expire" msgstr "Anzahl von Tagen nach denen das Passwort verfällt" -#: osusers/models.py:332 +#: osusers/models.py:333 msgid "Inactivity period" msgstr "Inaktivitätsperiode" -#: osusers/models.py:333 +#: osusers/models.py:335 msgid "" "The number of days after the password has expired during which the password " "should still be accepted" @@ -178,65 +178,62 @@ msgstr "" "Die Anzahl von Tagen für die ein verfallenes Passwort noch akzeptiert werden " "soll" -#: osusers/models.py:337 +#: osusers/models.py:342 msgid "Account expiration date" msgstr "Kontoverfallsdatum" -#: osusers/models.py:338 +#: osusers/models.py:344 msgid "" "The date of expiration of the account, expressed as number of days since Jan " "1, 1970" msgstr "Kontoverfallsdatum in Tagen seit dem 1. Januar 1970" -#: osusers/models.py:345 +#: osusers/models.py:355 msgid "Shadow password" msgstr "Shadow-Passwort" -#: osusers/models.py:346 +#: osusers/models.py:356 msgid "Shadow passwords" msgstr "Shadow-Passwörter" -#: osusers/models.py:372 +#: osusers/models.py:382 msgid "Additional group" msgstr "Weitere Gruppe" -#: osusers/models.py:373 +#: osusers/models.py:383 msgid "Additional groups" msgstr "Weitere Gruppen" -#: osusers/models.py:502 +#: osusers/models.py:514 msgid "Algorithm" msgstr "Algorithmus" -#: osusers/models.py:503 +#: osusers/models.py:515 msgid "Key bytes" msgstr "Schlüsselbytes" -#: osusers/models.py:504 +#: osusers/models.py:515 msgid "Base64 encoded key bytes" msgstr "Base64-kodierte Schlüsselbytes" -#: osusers/models.py:505 +#: osusers/models.py:516 msgid "Comment" msgstr "Kommentar" -#: osusers/models.py:510 +#: osusers/models.py:521 msgid "SSH public key" msgstr "Öffentlicher SSH-Schlüssel" -#: osusers/models.py:511 +#: osusers/models.py:522 msgid "SSH public keys" msgstr "Öffentliche SSH-Schlüssel" -#: osusers/views.py:56 +#: osusers/views.py:48 #, python-brace-format msgid "New password for {username} has been set successfully." msgstr "Für {username} wurde erfolgreich ein neues Passwort gesetzt." -#: osusers/views.py:92 +#: osusers/views.py:88 #, python-brace-format msgid "Successfully added new {algorithm} SSH public key." msgstr "Neuer {algorithm}-SSH-Schlüssel erfolgreich hinzugefügt." - -#~ msgid "Passwords don't match" -#~ msgstr "Passwörter stimmen nicht überein" diff --git a/gnuviechadmin/osusers/migrations/0001_initial.py b/gnuviechadmin/osusers/migrations/0001_initial.py index ddc97e8..ef00798 100644 --- a/gnuviechadmin/osusers/migrations/0001_initial.py +++ b/gnuviechadmin/osusers/migrations/0001_initial.py @@ -1,230 +1,383 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - import django.utils.timezone import model_utils.fields from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='AdditionalGroup', + name="AdditionalGroup", 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': 'Additional group', - 'verbose_name_plural': 'Additional groups', + "verbose_name": "Additional group", + "verbose_name_plural": "Additional groups", }, bases=(models.Model,), ), migrations.CreateModel( - name='DeleteTaskResult', + name="DeleteTaskResult", fields=[ - ('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)), - ('task_uuid', models.CharField( - max_length=64, serialize=False, primary_key=True)), - ('task_name', models.CharField(max_length=255, db_index=True)), - ('is_finished', models.BooleanField(default=False)), - ('is_success', models.BooleanField(default=False)), - ('state', models.CharField(max_length=10)), - ('result_body', models.TextField(blank=True)), - ('modeltype', models.CharField(max_length=20, db_index=True)), - ('modelname', models.CharField(max_length=255)), + ( + "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, + ), + ), + ( + "task_uuid", + models.CharField(max_length=64, serialize=False, primary_key=True), + ), + ("task_name", models.CharField(max_length=255, db_index=True)), + ("is_finished", models.BooleanField(default=False)), + ("is_success", models.BooleanField(default=False)), + ("state", models.CharField(max_length=10)), + ("result_body", models.TextField(blank=True)), + ("modeltype", models.CharField(max_length=20, db_index=True)), + ("modelname", models.CharField(max_length=255)), ], options={ - 'abstract': False, + "abstract": False, }, bases=(models.Model,), ), migrations.CreateModel( - name='Group', + name="Group", fields=[ - ('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)), - ('groupname', models.CharField( - unique=True, max_length=16, verbose_name='Group name')), - ('gid', models.PositiveSmallIntegerField( - unique=True, serialize=False, verbose_name='Group ID', - primary_key=True)), - ('descr', models.TextField( - verbose_name='Description', blank=True)), - ('passwd', models.CharField( - max_length=128, verbose_name='Group password', blank=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, + ), + ), + ( + "groupname", + models.CharField( + unique=True, max_length=16, verbose_name="Group name" + ), + ), + ( + "gid", + models.PositiveSmallIntegerField( + unique=True, + serialize=False, + verbose_name="Group ID", + primary_key=True, + ), + ), + ("descr", models.TextField(verbose_name="Description", blank=True)), + ( + "passwd", + models.CharField( + max_length=128, verbose_name="Group password", blank=True + ), + ), ], options={ - 'verbose_name': 'Group', - 'verbose_name_plural': 'Groups', + "verbose_name": "Group", + "verbose_name_plural": "Groups", }, bases=(models.Model,), ), migrations.CreateModel( - name='GroupTaskResult', + name="GroupTaskResult", fields=[ - ('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)), - ('task_uuid', models.CharField( - max_length=64, serialize=False, primary_key=True)), - ('task_name', models.CharField(max_length=255, db_index=True)), - ('is_finished', models.BooleanField(default=False)), - ('is_success', models.BooleanField(default=False)), - ('state', models.CharField(max_length=10)), - ('result_body', models.TextField(blank=True)), - ('group', models.ForeignKey( - to='osusers.Group', on_delete=models.CASCADE)), + ( + "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, + ), + ), + ( + "task_uuid", + models.CharField(max_length=64, serialize=False, primary_key=True), + ), + ("task_name", models.CharField(max_length=255, db_index=True)), + ("is_finished", models.BooleanField(default=False)), + ("is_success", models.BooleanField(default=False)), + ("state", models.CharField(max_length=10)), + ("result_body", models.TextField(blank=True)), + ( + "group", + models.ForeignKey(to="osusers.Group", on_delete=models.CASCADE), + ), ], options={ - 'abstract': False, + "abstract": False, }, bases=(models.Model,), ), migrations.CreateModel( - name='User', + name="User", fields=[ - ('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)), - ('username', models.CharField( - unique=True, max_length=64, verbose_name='User name')), - ('uid', models.PositiveSmallIntegerField( - unique=True, serialize=False, verbose_name='User ID', - primary_key=True)), - ('gecos', models.CharField( - max_length=128, verbose_name='Gecos field', blank=True)), - ('homedir', models.CharField( - max_length=256, verbose_name='Home directory')), - ('shell', models.CharField( - max_length=64, verbose_name='Login shell')), + ( + "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, + ), + ), + ( + "username", + models.CharField( + unique=True, max_length=64, verbose_name="User name" + ), + ), + ( + "uid", + models.PositiveSmallIntegerField( + unique=True, + serialize=False, + verbose_name="User ID", + primary_key=True, + ), + ), + ( + "gecos", + models.CharField( + max_length=128, verbose_name="Gecos field", blank=True + ), + ), + ( + "homedir", + models.CharField(max_length=256, verbose_name="Home directory"), + ), + ("shell", models.CharField(max_length=64, verbose_name="Login shell")), ], options={ - 'verbose_name': 'Benutzer', - 'verbose_name_plural': 'Users', + "verbose_name": "Benutzer", + "verbose_name_plural": "Users", }, bases=(models.Model,), ), migrations.CreateModel( - name='Shadow', + name="Shadow", fields=[ - ('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)), - ('user', models.OneToOneField( - primary_key=True, serialize=False, to='osusers.User', - verbose_name='Benutzer', on_delete=models.CASCADE)), - ('passwd', models.CharField( - max_length=128, verbose_name='Encrypted password')), - ('changedays', models.PositiveSmallIntegerField( - help_text='This is expressed in days since Jan 1, 1970', - null=True, verbose_name='Date of last change', blank=True)), - ('minage', models.PositiveSmallIntegerField( - help_text='Minimum number of days before the password can ' - 'be changed', - null=True, verbose_name='Minimum age', blank=True)), - ('maxage', models.PositiveSmallIntegerField( - help_text='Maximum number of days after which the ' - 'password has to be changed', - null=True, verbose_name='Maximum age', blank=True)), - ('gracedays', models.PositiveSmallIntegerField( - help_text='The number of days before the password is ' - 'going to expire', - null=True, verbose_name='Grace period', blank=True)), - ('inactdays', models.PositiveSmallIntegerField( - help_text='The number of days after the password has ' - 'expired during which the password should still ' - 'be accepted', - null=True, verbose_name='Inactivity period', blank=True)), - ('expiredays', models.PositiveSmallIntegerField( - default=None, - help_text='The date of expiration of the account, ' - 'expressed as number of days since Jan 1, 1970', - null=True, verbose_name='Account expiration date', - blank=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, + ), + ), + ( + "user", + models.OneToOneField( + primary_key=True, + serialize=False, + to="osusers.User", + verbose_name="Benutzer", + on_delete=models.CASCADE, + ), + ), + ( + "passwd", + models.CharField(max_length=128, verbose_name="Encrypted password"), + ), + ( + "changedays", + models.PositiveSmallIntegerField( + help_text="This is expressed in days since Jan 1, 1970", + null=True, + verbose_name="Date of last change", + blank=True, + ), + ), + ( + "minage", + models.PositiveSmallIntegerField( + help_text="Minimum number of days before the password can " + "be changed", + null=True, + verbose_name="Minimum age", + blank=True, + ), + ), + ( + "maxage", + models.PositiveSmallIntegerField( + help_text="Maximum number of days after which the " + "password has to be changed", + null=True, + verbose_name="Maximum age", + blank=True, + ), + ), + ( + "gracedays", + models.PositiveSmallIntegerField( + help_text="The number of days before the password is " + "going to expire", + null=True, + verbose_name="Grace period", + blank=True, + ), + ), + ( + "inactdays", + models.PositiveSmallIntegerField( + help_text="The number of days after the password has " + "expired during which the password should still " + "be accepted", + null=True, + verbose_name="Inactivity period", + blank=True, + ), + ), + ( + "expiredays", + models.PositiveSmallIntegerField( + default=None, + help_text="The date of expiration of the account, " + "expressed as number of days since Jan 1, 1970", + null=True, + verbose_name="Account expiration date", + blank=True, + ), + ), ], options={ - 'verbose_name': 'Shadow password', - 'verbose_name_plural': 'Shadow passwords', + "verbose_name": "Shadow password", + "verbose_name_plural": "Shadow passwords", }, bases=(models.Model,), ), migrations.CreateModel( - name='UserTaskResult', + name="UserTaskResult", fields=[ - ('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)), - ('task_uuid', models.CharField( - max_length=64, serialize=False, primary_key=True)), - ('task_name', models.CharField(max_length=255, db_index=True)), - ('is_finished', models.BooleanField(default=False)), - ('is_success', models.BooleanField(default=False)), - ('state', models.CharField(max_length=10)), - ('result_body', models.TextField(blank=True)), - ('user', models.ForeignKey( - to='osusers.User', on_delete=models.CASCADE)), + ( + "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, + ), + ), + ( + "task_uuid", + models.CharField(max_length=64, serialize=False, primary_key=True), + ), + ("task_name", models.CharField(max_length=255, db_index=True)), + ("is_finished", models.BooleanField(default=False)), + ("is_success", models.BooleanField(default=False)), + ("state", models.CharField(max_length=10)), + ("result_body", models.TextField(blank=True)), + ( + "user", + models.ForeignKey(to="osusers.User", on_delete=models.CASCADE), + ), ], options={ - 'abstract': False, + "abstract": False, }, bases=(models.Model,), ), migrations.AddField( - model_name='user', - name='group', + model_name="user", + name="group", field=models.ForeignKey( - verbose_name='Group', to='osusers.Group', - on_delete=models.CASCADE), + verbose_name="Group", to="osusers.Group", on_delete=models.CASCADE + ), preserve_default=True, ), migrations.AddField( - model_name='additionalgroup', - name='group', - field=models.ForeignKey( - to='osusers.Group', on_delete=models.CASCADE), + model_name="additionalgroup", + name="group", + field=models.ForeignKey(to="osusers.Group", on_delete=models.CASCADE), preserve_default=True, ), migrations.AddField( - model_name='additionalgroup', - name='user', - field=models.ForeignKey( - to='osusers.User', on_delete=models.CASCADE), + model_name="additionalgroup", + name="user", + field=models.ForeignKey(to="osusers.User", on_delete=models.CASCADE), preserve_default=True, ), migrations.AlterUniqueTogether( - name='additionalgroup', - unique_together={('user', 'group')}, + name="additionalgroup", + unique_together={("user", "group")}, ), ] diff --git a/gnuviechadmin/osusers/migrations/0002_auto_20141226_1456.py b/gnuviechadmin/osusers/migrations/0002_auto_20141226_1456.py index 0c90043..69d8c3a 100644 --- a/gnuviechadmin/osusers/migrations/0002_auto_20141226_1456.py +++ b/gnuviechadmin/osusers/migrations/0002_auto_20141226_1456.py @@ -1,31 +1,28 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations +from django.db import migrations class Migration(migrations.Migration): - dependencies = [ - ('osusers', '0001_initial'), + ("osusers", "0001_initial"), ] operations = [ migrations.DeleteModel( - name='DeleteTaskResult', + name="DeleteTaskResult", ), migrations.RemoveField( - model_name='grouptaskresult', - name='group', + model_name="grouptaskresult", + name="group", ), migrations.DeleteModel( - name='GroupTaskResult', + name="GroupTaskResult", ), migrations.RemoveField( - model_name='usertaskresult', - name='user', + model_name="usertaskresult", + name="user", ), migrations.DeleteModel( - name='UserTaskResult', + name="UserTaskResult", ), ] diff --git a/gnuviechadmin/osusers/migrations/0003_user_customer.py b/gnuviechadmin/osusers/migrations/0003_user_customer.py index 6226187..f228b0e 100644 --- a/gnuviechadmin/osusers/migrations/0003_user_customer.py +++ b/gnuviechadmin/osusers/migrations/0003_user_customer.py @@ -1,23 +1,21 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - -from django.db import models, migrations from django.conf import settings +from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('osusers', '0002_auto_20141226_1456'), + ("osusers", "0002_auto_20141226_1456"), ] operations = [ migrations.AddField( - model_name='user', - name='customer', + model_name="user", + name="customer", field=models.ForeignKey( - default=1, to=settings.AUTH_USER_MODEL, - on_delete=models.CASCADE), + default=1, to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE + ), preserve_default=False, ), ] diff --git a/gnuviechadmin/osusers/migrations/0004_auto_20150104_1751.py b/gnuviechadmin/osusers/migrations/0004_auto_20150104_1751.py index 288db36..324f394 100644 --- a/gnuviechadmin/osusers/migrations/0004_auto_20150104_1751.py +++ b/gnuviechadmin/osusers/migrations/0004_auto_20150104_1751.py @@ -1,25 +1,27 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('osusers', '0003_user_customer'), + ("osusers", "0003_user_customer"), ] operations = [ migrations.AlterModelOptions( - name='user', - options={'verbose_name': 'User', 'verbose_name_plural': 'Users'}, + name="user", + options={"verbose_name": "User", "verbose_name_plural": "Users"}, ), migrations.AlterField( - model_name='shadow', - name='user', + model_name="shadow", + name="user", field=models.OneToOneField( - primary_key=True, serialize=False, to='osusers.User', - verbose_name='User', on_delete=models.CASCADE), + primary_key=True, + serialize=False, + to="osusers.User", + verbose_name="User", + on_delete=models.CASCADE, + ), preserve_default=True, ), ] diff --git a/gnuviechadmin/osusers/migrations/0005_auto_20150131_2009.py b/gnuviechadmin/osusers/migrations/0005_auto_20150131_2009.py index d930c57..f0de2c4 100644 --- a/gnuviechadmin/osusers/migrations/0005_auto_20150131_2009.py +++ b/gnuviechadmin/osusers/migrations/0005_auto_20150131_2009.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - import django.utils.timezone import model_utils.fields from django.db import migrations, models @@ -8,41 +6,64 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('osusers', '0004_auto_20150104_1751'), + ("osusers", "0004_auto_20150104_1751"), ] operations = [ migrations.CreateModel( - name='SshPublicKey', + name="SshPublicKey", 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)), - ('algorithm', models.CharField( - max_length=20, verbose_name='Algorithm')), - ('data', models.TextField( - help_text='Base64 encoded key bytes', - verbose_name='Key bytes')), - ('comment', models.TextField( - verbose_name='Comment', blank=True)), - ('user', models.ForeignKey( - verbose_name='User', to='osusers.User', - 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, + ), + ), + ( + "algorithm", + models.CharField(max_length=20, verbose_name="Algorithm"), + ), + ( + "data", + models.TextField( + help_text="Base64 encoded key bytes", verbose_name="Key bytes" + ), + ), + ("comment", models.TextField(verbose_name="Comment", blank=True)), + ( + "user", + models.ForeignKey( + verbose_name="User", to="osusers.User", on_delete=models.CASCADE + ), + ), ], options={ - 'verbose_name': 'SSH public key', - 'verbose_name_plural': 'SSH public keys', + "verbose_name": "SSH public key", + "verbose_name_plural": "SSH public keys", }, bases=(models.Model,), ), migrations.AlterUniqueTogether( - name='sshpublickey', - unique_together={('user', 'algorithm', 'data')}, + name="sshpublickey", + unique_together={("user", "algorithm", "data")}, ), ] diff --git a/gnuviechadmin/osusers/models.py b/gnuviechadmin/osusers/models.py index 14d36ff..ff36f49 100644 --- a/gnuviechadmin/osusers/models.py +++ b/gnuviechadmin/osusers/models.py @@ -3,27 +3,24 @@ This module defines the database models of operating system users. """ import base64 -from datetime import date import logging import os +from datetime import date -from django.db import models, transaction from django.conf import settings from django.core.exceptions import ValidationError +from django.db import models, transaction from django.dispatch import Signal from django.utils import timezone -from django.utils.translation import ugettext as _ - +from django.utils.translation import gettext as _ from model_utils.models import TimeStampedModel - -from passlib.hash import sha512_crypt -from passlib.utils import generate_password - +from passlib.handlers.sha2_crypt import sha512_crypt +from passlib.pwd import genword _LOGGER = logging.getLogger(__name__) -password_set = Signal(providing_args=["instance", "password"]) +password_set = Signal() CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL = _("You can not use a user's primary group.") @@ -150,7 +147,7 @@ class UserManager(models.Manager): If username is None the result of :py:meth:`get_next_username` is used. If password is None a new password will be generated using passlib's - :py:func:`generate_password`. + :py:func:`genword`. :param customer: Django User instance this user is associated to :param str username: the username or None @@ -166,7 +163,7 @@ class UserManager(models.Manager): if username is None: username = self.get_next_username() if password is None: - password = generate_password() + password = genword(entropy=128) homedir = os.path.join(settings.OSUSER_HOME_BASEPATH, username) group = Group.objects.create(groupname=username, gid=gid) user = self.create( @@ -178,8 +175,7 @@ class UserManager(models.Manager): shell=settings.OSUSER_DEFAULT_SHELL, ) user.set_password(password) - if commit: - user.save() + user.save() return user @@ -368,7 +364,7 @@ class Shadow(TimeStampedModel, models.Model): :param str password: the password """ - self.passwd = sha512_crypt.encrypt(password) + self.passwd = sha512_crypt.hash(password) class AdditionalGroup(TimeStampedModel, models.Model): diff --git a/gnuviechadmin/osusers/signals.py b/gnuviechadmin/osusers/signals.py index e062cb9..3ee4079 100644 --- a/gnuviechadmin/osusers/signals.py +++ b/gnuviechadmin/osusers/signals.py @@ -3,17 +3,14 @@ This module contains the signal handlers of the :py:mod:`osusers` app. The module starts Celery_ tasks. -.. _Celery: http://www.celeryproject.org/ +.. _Celery: https://www.celeryproject.org/ """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import import logging -from django.db.models.signals import ( - post_delete, - post_save, -) +from django.db.models.signals import post_delete, post_save from django.dispatch import receiver from fileservertasks.tasks import ( @@ -34,14 +31,7 @@ from ldaptasks.tasks import ( ) from taskresults.models import TaskResult -from .models import ( - AdditionalGroup, - Group, - SshPublicKey, - User, - password_set, -) - +from .models import AdditionalGroup, Group, SshPublicKey, User, password_set _LOGGER = logging.getLogger(__name__) @@ -76,11 +66,12 @@ def handle_user_password_set(sender, instance, password, **kwargs): } """ taskresult = TaskResult.objects.create_task_result( - 'handle_user_password_set', - set_ldap_user_password.s(instance.username, password)) + "handle_user_password_set", + set_ldap_user_password.s(instance.username, password), + ) _LOGGER.info( - 'LDAP password change has been requested in task %s', - taskresult.task_id) + "LDAP password change has been requested in task %s", taskresult.task_id + ) @receiver(post_save, sender=Group) @@ -114,14 +105,13 @@ def handle_group_created(sender, instance, created, **kwargs): """ if created: taskresult = TaskResult.objects.create_task_result( - 'handle_group_created', - create_ldap_group.s( - instance.groupname, instance.gid, instance.descr)) + "handle_group_created", + create_ldap_group.s(instance.groupname, instance.gid, instance.descr), + ) _LOGGER.info( - 'LDAP group creation has been requested in task %s', - taskresult.task_id) - _LOGGER.debug( - 'group %s has been %s', instance, created and "created" or "updated") + "LDAP group creation has been requested in task %s", taskresult.task_id + ) + _LOGGER.debug("group %s has been %s", instance, created and "created" or "updated") @receiver(post_save, sender=User) @@ -167,18 +157,27 @@ def handle_user_created(sender, instance, created, **kwargs): """ if created: - chain = create_ldap_user.s( - instance.username, instance.uid, instance.group.gid, - instance.gecos, instance.homedir, instance.shell, None - ) | setup_file_sftp_userdir_chained.s() | ( - setup_file_mail_userdir_chained.s()) - taskresult = TaskResult.objects.create_task_result( - 'handle_user_created', chain) + task_ldap_1 = create_ldap_user.s( + instance.username, + instance.uid, + instance.group.gid, + instance.gecos, + instance.homedir, + instance.shell, + None, + ).set(queue="ldap") + task_file_1 = setup_file_sftp_userdir_chained.s().set(queue="file") + task_file_2 = setup_file_mail_userdir_chained.s().set(queue="file") + + chain = task_ldap_1 | task_file_1 | task_file_2 + + task_result = TaskResult.objects.create_task_result( + "handle_user_created", chain + ) _LOGGER.info( - 'LDAP user creation has been requested in task %s', - taskresult.task_id) - _LOGGER.debug( - 'user %s has been %s', instance, created and "created" or "updated") + "LDAP user creation has been requested in task %s", task_result.task_id + ) + _LOGGER.debug("user %s has been %s", instance, created and "created" or "updated") @receiver(post_save, sender=AdditionalGroup) @@ -213,12 +212,13 @@ def handle_user_added_to_group(sender, instance, created, **kwargs): """ if created: taskresult = TaskResult.objects.create_task_result( - 'handle_user_added_to_group', - add_ldap_user_to_group.s( - instance.user.username, instance.group.groupname)) + "handle_user_added_to_group", + add_ldap_user_to_group.s(instance.user.username, instance.group.groupname), + ) _LOGGER.info( - 'Adding user to LDAP group has been requested in task %s', - taskresult.task_id) + "Adding user to LDAP group has been requested in task %s", + taskresult.task_id, + ) @receiver(post_save, sender=SshPublicKey) @@ -252,21 +252,11 @@ def handle_ssh_keys_changed(sender, instance, **kwargs): """ sig = set_file_ssh_authorized_keys.s( - instance.user.username, [ - str(key) for key in - SshPublicKey.objects.filter(user=instance.user)]) - taskresult = TaskResult.objects.create_task_result( - 'handle_ssh_keys_changed', sig) - _LOGGER.info( - 'Change of SSH keys has been requested in task %s', - taskresult.task_id) - - -# @receiver(post_delete) -# def handle_post_delete(sender, **kwargs): -# _LOGGER.debug( -# 'handling post_delete signal for %s with args %s', -# sender, kwargs) + instance.user.username, + [str(key) for key in SshPublicKey.objects.filter(user=instance.user)], + ) + taskresult = TaskResult.objects.create_task_result("handle_ssh_keys_changed", sig) + _LOGGER.info("Change of SSH keys has been requested in task %s", taskresult.task_id) @receiver(post_delete, sender=Group) @@ -299,11 +289,11 @@ def handle_group_deleted(sender, instance, **kwargs): """ taskresult = TaskResult.objects.create_task_result( - 'handle_group_deleted', - delete_ldap_group.s(instance.groupname)) + "handle_group_deleted", delete_ldap_group.s(instance.groupname) + ) _LOGGER.info( - 'LDAP group deletion has been requested in task %s', - taskresult.task_id) + "LDAP group deletion has been requested in task %s", taskresult.task_id + ) @receiver(post_delete, sender=User) @@ -348,15 +338,17 @@ def handle_user_deleted(sender, instance, **kwargs): } """ - chain = delete_file_mail_userdir.s( - instance.username - ) | delete_file_sftp_userdir_chained.s() | delete_ldap_user_chained.s() - _LOGGER.debug('chain signature %s', chain) - taskresult = TaskResult.objects.create_task_result( - 'handle_user_deleted', chain) + file_task_1 = delete_file_mail_userdir.s(instance.username).set(queue="file") + file_task_2 = delete_file_sftp_userdir_chained.s().set(queue="file") + ldap_task_1 = delete_ldap_user_chained.s().set(queue="ldap") + + chain = file_task_1 | file_task_2 | ldap_task_1 + _LOGGER.debug("chain signature %s", chain) + task_result = TaskResult.objects.create_task_result("handle_user_deleted", chain) + _LOGGER.info( - 'LDAP user deletion has been requested in task %s', - taskresult.task_id) + "LDAP user deletion has been requested in task %s", task_result.task_id + ) @receiver(post_delete, sender=AdditionalGroup) @@ -393,9 +385,10 @@ def handle_user_removed_from_group(sender, instance, **kwargs): """ taskresult = TaskResult.objects.create_task_result( - 'handle_user_removed_from_group', - remove_ldap_user_from_group.s( - instance.user.username, instance.group.groupname)) + "handle_user_removed_from_group", + remove_ldap_user_from_group.s(instance.user.username, instance.group.groupname), + ) _LOGGER.info( - 'Removing user from LDAP group has been requested in task %s', - taskresult.task_id) + "Removing user from LDAP group has been requested in task %s", + taskresult.task_id, + ) diff --git a/gnuviechadmin/osusers/tests/test_models.py b/gnuviechadmin/osusers/tests/test_models.py index 8e0439a..c76178a 100644 --- a/gnuviechadmin/osusers/tests/test_models.py +++ b/gnuviechadmin/osusers/tests/test_models.py @@ -7,11 +7,11 @@ from django.core.exceptions import ValidationError from django.test import TestCase from django.test.utils import override_settings from django.utils import timezone -from passlib.hash import sha512_crypt +from passlib.handlers.sha2_crypt import sha512_crypt from osusers.models import ( - AdditionalGroup, CANNOT_USE_PRIMARY_GROUP_AS_ADDITIONAL, + AdditionalGroup, Group, Shadow, SshPublicKey, @@ -212,7 +212,7 @@ class GroupTest(TestCaseWithCeleryTasks): group.delete() self.assertEqual(len(Group.objects.all()), 0) self.assertEqual(len(TaskResult.objects.all()), 2) - tr = TaskResult.objects.first() + tr = TaskResult.objects.order_by("created").first() self.assertEqual(tr.creator, "handle_group_created") @@ -529,7 +529,7 @@ class SshPublicKeyManagerTest(TestCaseWithCeleryTasks): def test_parse_keytext_openssh(self): res = SshPublicKey.objects.parse_key_text(EXAMPLE_KEY_4_OPENSSH) - self.assertEquals(len(res), 3) + self.assertEqual(len(res), 3) self.assertEqual(res[0], "ssh-rsa") self.assertGreater(len(res[1]), 40) self.assertEqual(res[2], "") diff --git a/gnuviechadmin/osusers/tests/test_views.py b/gnuviechadmin/osusers/tests/test_views.py index 7c0d2bb..1565052 100644 --- a/gnuviechadmin/osusers/tests/test_views.py +++ b/gnuviechadmin/osusers/tests/test_views.py @@ -3,18 +3,16 @@ This module provides tests for :py:mod:`osusers.views`. """ -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch -from django.test import TestCase, TransactionTestCase from django.contrib.auth import get_user_model +from django.test import TestCase, TransactionTestCase from django.urls import reverse from hostingpackages.models import CustomerHostingPackage, HostingPackageTemplate - from osusers.models import SshPublicKey from osusers.views import AddSshPublicKey, DeleteSshPublicKey, EditSshPublicKeyComment - User = get_user_model() TEST_USER = "test" @@ -31,7 +29,6 @@ EXAMPLE_KEY = "".join( class HostingPackageAwareTestMixin(object): - # noinspection PyMethodMayBeStatic def _setup_hosting_package(self, customer): template = HostingPackageTemplate.objects.create( @@ -169,7 +166,7 @@ class DeleteSshPublicKeyTest(HostingPackageAwareTestMixin, TestCase): kwargs={"package": str(self.package.pk), "pk": str(self.sshkey.pk)}, ) queryset = view.get_queryset() - self.assertQuerysetEqual(queryset, [repr(self.sshkey)]) + self.assertQuerysetEqual(queryset, [repr(self.sshkey)], transform=repr) def test_get_context_data(self): self.client.login(username=TEST_USER, password=TEST_PASSWORD) @@ -237,7 +234,7 @@ class EditSshPublicKeyCommentTest(HostingPackageAwareTestMixin, TransactionTestC kwargs={"package": str(self.package.pk), "pk": str(self.sshkey.pk)}, ) queryset = view.get_queryset() - self.assertQuerysetEqual(queryset, [repr(self.sshkey)]) + self.assertQuerysetEqual(queryset, [repr(self.sshkey)], transform=repr) def test_get_form_kwargs(self): self.client.login(username=TEST_USER, password=TEST_PASSWORD) diff --git a/gnuviechadmin/osusers/tests/testutils.py b/gnuviechadmin/osusers/tests/testutils.py new file mode 100644 index 0000000..dcee73e --- /dev/null +++ b/gnuviechadmin/osusers/tests/testutils.py @@ -0,0 +1,13 @@ +from django.contrib import auth + +from osusers.models import User + + +def create_test_customer(): + customer_model = auth.get_user_model() + return customer_model.objects.create_user("test_customer", "testuser@example.org", "test_password") + + +def create_test_user(): + customer = create_test_customer() + return User.objects.create_user(customer) diff --git a/gnuviechadmin/osusers/urls.py b/gnuviechadmin/osusers/urls.py index 086a2bf..bf7f80a 100644 --- a/gnuviechadmin/osusers/urls.py +++ b/gnuviechadmin/osusers/urls.py @@ -2,9 +2,9 @@ This module defines the URL patterns for operating system user related views. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import -from django.conf.urls import url +from django.urls import re_path from .views import ( AddSshPublicKey, @@ -14,16 +14,30 @@ from .views import ( SetOsUserPassword, ) - urlpatterns = [ - url(r'^(?P[\w0-9@.+-_]+)/setpassword$', SetOsUserPassword.as_view(), - name='set_osuser_password'), - url(r'^(?P\d+)/ssh-keys/$', ListSshPublicKeys.as_view(), - name='list_ssh_keys'), - url(r'^(?P\d+)/ssh-keys/add$', AddSshPublicKey.as_view(), - name='add_ssh_key'), - url(r'^(?P\d+)/ssh-keys/(?P\d+)/edit-comment$', - EditSshPublicKeyComment.as_view(), name='edit_ssh_key_comment'), - url(r'^(?P\d+)/ssh-keys/(?P\d+)/delete$', - DeleteSshPublicKey.as_view(), name='delete_ssh_key'), + re_path( + r"^(?P[\w0-9@.+-_]+)/setpassword$", + SetOsUserPassword.as_view(), + name="set_osuser_password", + ), + re_path( + r"^(?P\d+)/ssh-keys/$", + ListSshPublicKeys.as_view(), + name="list_ssh_keys", + ), + re_path( + r"^(?P\d+)/ssh-keys/add$", + AddSshPublicKey.as_view(), + name="add_ssh_key", + ), + re_path( + r"^(?P\d+)/ssh-keys/(?P\d+)/edit-comment$", + EditSshPublicKeyComment.as_view(), + name="edit_ssh_key_comment", + ), + re_path( + r"^(?P\d+)/ssh-keys/(?P\d+)/delete$", + DeleteSshPublicKey.as_view(), + name="delete_ssh_key", + ), ] diff --git a/gnuviechadmin/osusers/views.py b/gnuviechadmin/osusers/views.py index 3a0a938..e04be72 100644 --- a/gnuviechadmin/osusers/views.py +++ b/gnuviechadmin/osusers/views.py @@ -2,20 +2,15 @@ This module defines the views for gnuviechadmin operating system user handling. """ -from __future__ import unicode_literals, absolute_import +from __future__ import absolute_import +from django.contrib import messages from django.shortcuts import redirect from django.urls import reverse -from django.views.generic import ( - CreateView, - DeleteView, - ListView, - UpdateView, -) -from django.utils.translation import ugettext as _ -from django.contrib import messages - +from django.utils.translation import gettext as _ +from django.views.generic import CreateView, DeleteView, ListView, UpdateView from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin + from gvawebcore.views import HostingPackageAndCustomerMixin from .forms import ( @@ -23,10 +18,7 @@ from .forms import ( ChangeOsUserPasswordForm, EditSshPublicKeyCommentForm, ) -from .models import ( - SshPublicKey, - User, -) +from .models import SshPublicKey, User class SetOsUserPassword(StaffOrSelfLoginRequiredMixin, UpdateView): @@ -34,19 +26,19 @@ class SetOsUserPassword(StaffOrSelfLoginRequiredMixin, UpdateView): This view is used for setting a new operating system user password. """ + model = User - slug_field = 'username' - template_name_suffix = '_setpassword' - context_object_name = 'osuser' + slug_field = "username" + template_name_suffix = "_setpassword" + context_object_name = "osuser" form_class = ChangeOsUserPasswordForm def get_customer_object(self): return self.get_object().customer def get_context_data(self, *args, **kwargs): - context = super(SetOsUserPassword, self).get_context_data( - *args, **kwargs) - context['customer'] = self.get_customer_object() + context = super(SetOsUserPassword, self).get_context_data(*args, **kwargs) + context["customer"] = self.get_customer_object() return context def form_valid(self, form): @@ -55,7 +47,8 @@ class SetOsUserPassword(StaffOrSelfLoginRequiredMixin, UpdateView): self.request, _("New password for {username} has been set successfully.").format( username=osuser.username - )) + ), + ) return redirect(osuser.customerhostingpackage) @@ -67,30 +60,34 @@ class AddSshPublicKey( operating system user. """ + model = SshPublicKey - context_object_name = 'key' - template_name_suffix = '_create' + context_object_name = "key" + template_name_suffix = "_create" form_class = AddSshPublicKeyForm def get_form_kwargs(self): kwargs = super(AddSshPublicKey, self).get_form_kwargs() - kwargs['hostingpackage'] = self.get_hosting_package() + kwargs["hostingpackage"] = self.get_hosting_package() return kwargs def get_context_data(self, **kwargs): context = super(AddSshPublicKey, self).get_context_data(**kwargs) - context.update({ - 'customer': self.get_customer_object(), - 'osuser': self.get_hosting_package().osuser.username, - }) + context.update( + { + "customer": self.get_customer_object(), + "osuser": self.get_hosting_package().osuser.username, + } + ) return context def form_valid(self, form): key = form.save() messages.success( self.request, - _('Successfully added new {algorithm} SSH public key.').format( - algorithm=key.algorithm) + _("Successfully added new {algorithm} SSH public key.").format( + algorithm=key.algorithm + ), ) return redirect(self.get_hosting_package()) @@ -104,20 +101,22 @@ class ListSshPublicKeys( via URL parameter 'pattern'. """ + model = SshPublicKey - context_object_name = 'keys' + context_object_name = "keys" def get_queryset(self): - return SshPublicKey.objects.filter( - user=self.get_hosting_package().osuser) + return SshPublicKey.objects.filter(user=self.get_hosting_package().osuser) def get_context_data(self, **kwargs): context = super(ListSshPublicKeys, self).get_context_data(**kwargs) - context.update({ - 'hostingpackage': self.get_hosting_package(), - 'customer': self.get_customer_object(), - 'osuser': self.get_hosting_package().osuser.username, - }) + context.update( + { + "hostingpackage": self.get_hosting_package(), + "customer": self.get_customer_object(), + "osuser": self.get_hosting_package().osuser.username, + } + ) return context @@ -131,24 +130,29 @@ class DeleteSshPublicKey( """ model = SshPublicKey - context_object_name = 'key' + context_object_name = "key" def get_queryset(self): - return super(DeleteSshPublicKey, self).get_queryset().filter( - user=self.get_hosting_package().osuser) + return ( + super(DeleteSshPublicKey, self) + .get_queryset() + .filter(user=self.get_hosting_package().osuser) + ) def get_context_data(self, **kwargs): context = super(DeleteSshPublicKey, self).get_context_data(**kwargs) - context.update({ - 'hostingpackage': self.get_hosting_package(), - 'customer': self.get_customer_object(), - 'osuser': self.get_hosting_package().osuser.username, - }) + context.update( + { + "hostingpackage": self.get_hosting_package(), + "customer": self.get_customer_object(), + "osuser": self.get_hosting_package().osuser.username, + } + ) return context def get_success_url(self): return reverse( - 'list_ssh_keys', kwargs={'package': self.get_hosting_package().id} + "list_ssh_keys", kwargs={"package": self.get_hosting_package().id} ) @@ -160,31 +164,36 @@ class EditSshPublicKeyComment( key `. """ + model = SshPublicKey - context_object_name = 'key' - template_name_suffix = '_edit_comment' + context_object_name = "key" + template_name_suffix = "_edit_comment" form_class = EditSshPublicKeyCommentForm def get_queryset(self): - return super(EditSshPublicKeyComment, self).get_queryset().filter( - user=self.get_hosting_package().osuser) + return ( + super(EditSshPublicKeyComment, self) + .get_queryset() + .filter(user=self.get_hosting_package().osuser) + ) def get_form_kwargs(self): kwargs = super(EditSshPublicKeyComment, self).get_form_kwargs() - kwargs['hostingpackage'] = self.get_hosting_package() + kwargs["hostingpackage"] = self.get_hosting_package() return kwargs def get_context_data(self, **kwargs): - context = super(EditSshPublicKeyComment, self).get_context_data( - **kwargs) - context.update({ - 'hostingpackage': self.get_hosting_package(), - 'customer': self.get_customer_object(), - 'osuser': self.get_hosting_package().osuser.username, - }) + context = super(EditSshPublicKeyComment, self).get_context_data(**kwargs) + context.update( + { + "hostingpackage": self.get_hosting_package(), + "customer": self.get_customer_object(), + "osuser": self.get_hosting_package().osuser.username, + } + ) return context def get_success_url(self): return reverse( - 'list_ssh_keys', kwargs={'package': self.get_hosting_package().id} + "list_ssh_keys", kwargs={"package": self.get_hosting_package().id} ) diff --git a/gnuviechadmin/static/css/bootstrap-icons.css b/gnuviechadmin/static/css/bootstrap-icons.css new file mode 100644 index 0000000..ace876c --- /dev/null +++ b/gnuviechadmin/static/css/bootstrap-icons.css @@ -0,0 +1,5882 @@ +@charset "UTF-8"; +/*! + * Bootstrap Icons (https://icons.getbootstrap.com/) + * Copyright 2019-2023 The Bootstrap Authors + * Licensed under MIT (https://github.com/twbs/icons/blob/main/LICENSE.md) + */ +@font-face { + font-display: block; + font-family: "bootstrap-icons"; + src: url("../fonts/bootstrap-icons.woff2?24e3eb84d0bcaf83d77f904c78ac1f47") format("woff2"), url("../fonts/bootstrap-icons.woff?24e3eb84d0bcaf83d77f904c78ac1f47") format("woff"); } +.bi::before, +[class^="bi-"]::before, +[class*=" bi-"]::before { + display: inline-block; + font-family: "bootstrap-icons" !important; + font-style: normal; + font-weight: normal !important; + font-variant: normal; + text-transform: none; + line-height: 1; + vertical-align: -.125em; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } + +.bi-123::before { + content: ""; } + +.bi-alarm-fill::before { + content: ""; } + +.bi-alarm::before { + content: ""; } + +.bi-align-bottom::before { + content: ""; } + +.bi-align-center::before { + content: ""; } + +.bi-align-end::before { + content: ""; } + +.bi-align-middle::before { + content: ""; } + +.bi-align-start::before { + content: ""; } + +.bi-align-top::before { + content: ""; } + +.bi-alt::before { + content: ""; } + +.bi-app-indicator::before { + content: ""; } + +.bi-app::before { + content: ""; } + +.bi-archive-fill::before { + content: ""; } + +.bi-archive::before { + content: ""; } + +.bi-arrow-90deg-down::before { + content: ""; } + +.bi-arrow-90deg-left::before { + content: ""; } + +.bi-arrow-90deg-right::before { + content: ""; } + +.bi-arrow-90deg-up::before { + content: ""; } + +.bi-arrow-bar-down::before { + content: ""; } + +.bi-arrow-bar-left::before { + content: ""; } + +.bi-arrow-bar-right::before { + content: ""; } + +.bi-arrow-bar-up::before { + content: ""; } + +.bi-arrow-clockwise::before { + content: ""; } + +.bi-arrow-counterclockwise::before { + content: ""; } + +.bi-arrow-down-circle-fill::before { + content: ""; } + +.bi-arrow-down-circle::before { + content: ""; } + +.bi-arrow-down-left-circle-fill::before { + content: ""; } + +.bi-arrow-down-left-circle::before { + content: ""; } + +.bi-arrow-down-left-square-fill::before { + content: ""; } + +.bi-arrow-down-left-square::before { + content: ""; } + +.bi-arrow-down-left::before { + content: ""; } + +.bi-arrow-down-right-circle-fill::before { + content: ""; } + +.bi-arrow-down-right-circle::before { + content: ""; } + +.bi-arrow-down-right-square-fill::before { + content: ""; } + +.bi-arrow-down-right-square::before { + content: ""; } + +.bi-arrow-down-right::before { + content: ""; } + +.bi-arrow-down-short::before { + content: ""; } + +.bi-arrow-down-square-fill::before { + content: ""; } + +.bi-arrow-down-square::before { + content: ""; } + +.bi-arrow-down-up::before { + content: ""; } + +.bi-arrow-down::before { + content: ""; } + +.bi-arrow-left-circle-fill::before { + content: ""; } + +.bi-arrow-left-circle::before { + content: ""; } + +.bi-arrow-left-right::before { + content: ""; } + +.bi-arrow-left-short::before { + content: ""; } + +.bi-arrow-left-square-fill::before { + content: ""; } + +.bi-arrow-left-square::before { + content: ""; } + +.bi-arrow-left::before { + content: ""; } + +.bi-arrow-repeat::before { + content: ""; } + +.bi-arrow-return-left::before { + content: ""; } + +.bi-arrow-return-right::before { + content: ""; } + +.bi-arrow-right-circle-fill::before { + content: ""; } + +.bi-arrow-right-circle::before { + content: ""; } + +.bi-arrow-right-short::before { + content: ""; } + +.bi-arrow-right-square-fill::before { + content: ""; } + +.bi-arrow-right-square::before { + content: ""; } + +.bi-arrow-right::before { + content: ""; } + +.bi-arrow-up-circle-fill::before { + content: ""; } + +.bi-arrow-up-circle::before { + content: ""; } + +.bi-arrow-up-left-circle-fill::before { + content: ""; } + +.bi-arrow-up-left-circle::before { + content: ""; } + +.bi-arrow-up-left-square-fill::before { + content: ""; } + +.bi-arrow-up-left-square::before { + content: ""; } + +.bi-arrow-up-left::before { + content: ""; } + +.bi-arrow-up-right-circle-fill::before { + content: ""; } + +.bi-arrow-up-right-circle::before { + content: ""; } + +.bi-arrow-up-right-square-fill::before { + content: ""; } + +.bi-arrow-up-right-square::before { + content: ""; } + +.bi-arrow-up-right::before { + content: ""; } + +.bi-arrow-up-short::before { + content: ""; } + +.bi-arrow-up-square-fill::before { + content: ""; } + +.bi-arrow-up-square::before { + content: ""; } + +.bi-arrow-up::before { + content: ""; } + +.bi-arrows-angle-contract::before { + content: ""; } + +.bi-arrows-angle-expand::before { + content: ""; } + +.bi-arrows-collapse::before { + content: ""; } + +.bi-arrows-expand::before { + content: ""; } + +.bi-arrows-fullscreen::before { + content: ""; } + +.bi-arrows-move::before { + content: ""; } + +.bi-aspect-ratio-fill::before { + content: ""; } + +.bi-aspect-ratio::before { + content: ""; } + +.bi-asterisk::before { + content: ""; } + +.bi-at::before { + content: ""; } + +.bi-award-fill::before { + content: ""; } + +.bi-award::before { + content: ""; } + +.bi-back::before { + content: ""; } + +.bi-backspace-fill::before { + content: ""; } + +.bi-backspace-reverse-fill::before { + content: ""; } + +.bi-backspace-reverse::before { + content: ""; } + +.bi-backspace::before { + content: ""; } + +.bi-badge-3d-fill::before { + content: ""; } + +.bi-badge-3d::before { + content: ""; } + +.bi-badge-4k-fill::before { + content: ""; } + +.bi-badge-4k::before { + content: ""; } + +.bi-badge-8k-fill::before { + content: ""; } + +.bi-badge-8k::before { + content: ""; } + +.bi-badge-ad-fill::before { + content: ""; } + +.bi-badge-ad::before { + content: ""; } + +.bi-badge-ar-fill::before { + content: ""; } + +.bi-badge-ar::before { + content: ""; } + +.bi-badge-cc-fill::before { + content: ""; } + +.bi-badge-cc::before { + content: ""; } + +.bi-badge-hd-fill::before { + content: ""; } + +.bi-badge-hd::before { + content: ""; } + +.bi-badge-tm-fill::before { + content: ""; } + +.bi-badge-tm::before { + content: ""; } + +.bi-badge-vo-fill::before { + content: ""; } + +.bi-badge-vo::before { + content: ""; } + +.bi-badge-vr-fill::before { + content: ""; } + +.bi-badge-vr::before { + content: ""; } + +.bi-badge-wc-fill::before { + content: ""; } + +.bi-badge-wc::before { + content: ""; } + +.bi-bag-check-fill::before { + content: ""; } + +.bi-bag-check::before { + content: ""; } + +.bi-bag-dash-fill::before { + content: ""; } + +.bi-bag-dash::before { + content: ""; } + +.bi-bag-fill::before { + content: ""; } + +.bi-bag-plus-fill::before { + content: ""; } + +.bi-bag-plus::before { + content: ""; } + +.bi-bag-x-fill::before { + content: ""; } + +.bi-bag-x::before { + content: ""; } + +.bi-bag::before { + content: ""; } + +.bi-bar-chart-fill::before { + content: ""; } + +.bi-bar-chart-line-fill::before { + content: ""; } + +.bi-bar-chart-line::before { + content: ""; } + +.bi-bar-chart-steps::before { + content: ""; } + +.bi-bar-chart::before { + content: ""; } + +.bi-basket-fill::before { + content: ""; } + +.bi-basket::before { + content: ""; } + +.bi-basket2-fill::before { + content: ""; } + +.bi-basket2::before { + content: ""; } + +.bi-basket3-fill::before { + content: ""; } + +.bi-basket3::before { + content: ""; } + +.bi-battery-charging::before { + content: ""; } + +.bi-battery-full::before { + content: ""; } + +.bi-battery-half::before { + content: ""; } + +.bi-battery::before { + content: ""; } + +.bi-bell-fill::before { + content: ""; } + +.bi-bell::before { + content: ""; } + +.bi-bezier::before { + content: ""; } + +.bi-bezier2::before { + content: ""; } + +.bi-bicycle::before { + content: ""; } + +.bi-binoculars-fill::before { + content: ""; } + +.bi-binoculars::before { + content: ""; } + +.bi-blockquote-left::before { + content: ""; } + +.bi-blockquote-right::before { + content: ""; } + +.bi-book-fill::before { + content: ""; } + +.bi-book-half::before { + content: ""; } + +.bi-book::before { + content: ""; } + +.bi-bookmark-check-fill::before { + content: ""; } + +.bi-bookmark-check::before { + content: ""; } + +.bi-bookmark-dash-fill::before { + content: ""; } + +.bi-bookmark-dash::before { + content: ""; } + +.bi-bookmark-fill::before { + content: ""; } + +.bi-bookmark-heart-fill::before { + content: ""; } + +.bi-bookmark-heart::before { + content: ""; } + +.bi-bookmark-plus-fill::before { + content: ""; } + +.bi-bookmark-plus::before { + content: ""; } + +.bi-bookmark-star-fill::before { + content: ""; } + +.bi-bookmark-star::before { + content: ""; } + +.bi-bookmark-x-fill::before { + content: ""; } + +.bi-bookmark-x::before { + content: ""; } + +.bi-bookmark::before { + content: ""; } + +.bi-bookmarks-fill::before { + content: ""; } + +.bi-bookmarks::before { + content: ""; } + +.bi-bookshelf::before { + content: ""; } + +.bi-bootstrap-fill::before { + content: ""; } + +.bi-bootstrap-reboot::before { + content: ""; } + +.bi-bootstrap::before { + content: ""; } + +.bi-border-all::before { + content: ""; } + +.bi-border-bottom::before { + content: ""; } + +.bi-border-center::before { + content: ""; } + +.bi-border-inner::before { + content: ""; } + +.bi-border-left::before { + content: ""; } + +.bi-border-middle::before { + content: ""; } + +.bi-border-outer::before { + content: ""; } + +.bi-border-right::before { + content: ""; } + +.bi-border-style::before { + content: ""; } + +.bi-border-top::before { + content: ""; } + +.bi-border-width::before { + content: ""; } + +.bi-border::before { + content: ""; } + +.bi-bounding-box-circles::before { + content: ""; } + +.bi-bounding-box::before { + content: ""; } + +.bi-box-arrow-down-left::before { + content: ""; } + +.bi-box-arrow-down-right::before { + content: ""; } + +.bi-box-arrow-down::before { + content: ""; } + +.bi-box-arrow-in-down-left::before { + content: ""; } + +.bi-box-arrow-in-down-right::before { + content: ""; } + +.bi-box-arrow-in-down::before { + content: ""; } + +.bi-box-arrow-in-left::before { + content: ""; } + +.bi-box-arrow-in-right::before { + content: ""; } + +.bi-box-arrow-in-up-left::before { + content: ""; } + +.bi-box-arrow-in-up-right::before { + content: ""; } + +.bi-box-arrow-in-up::before { + content: ""; } + +.bi-box-arrow-left::before { + content: ""; } + +.bi-box-arrow-right::before { + content: ""; } + +.bi-box-arrow-up-left::before { + content: ""; } + +.bi-box-arrow-up-right::before { + content: ""; } + +.bi-box-arrow-up::before { + content: ""; } + +.bi-box-seam::before { + content: ""; } + +.bi-box::before { + content: ""; } + +.bi-braces::before { + content: ""; } + +.bi-bricks::before { + content: ""; } + +.bi-briefcase-fill::before { + content: ""; } + +.bi-briefcase::before { + content: ""; } + +.bi-brightness-alt-high-fill::before { + content: ""; } + +.bi-brightness-alt-high::before { + content: ""; } + +.bi-brightness-alt-low-fill::before { + content: ""; } + +.bi-brightness-alt-low::before { + content: ""; } + +.bi-brightness-high-fill::before { + content: ""; } + +.bi-brightness-high::before { + content: ""; } + +.bi-brightness-low-fill::before { + content: ""; } + +.bi-brightness-low::before { + content: ""; } + +.bi-broadcast-pin::before { + content: ""; } + +.bi-broadcast::before { + content: ""; } + +.bi-brush-fill::before { + content: ""; } + +.bi-brush::before { + content: ""; } + +.bi-bucket-fill::before { + content: ""; } + +.bi-bucket::before { + content: ""; } + +.bi-bug-fill::before { + content: ""; } + +.bi-bug::before { + content: ""; } + +.bi-building::before { + content: ""; } + +.bi-bullseye::before { + content: ""; } + +.bi-calculator-fill::before { + content: ""; } + +.bi-calculator::before { + content: ""; } + +.bi-calendar-check-fill::before { + content: ""; } + +.bi-calendar-check::before { + content: ""; } + +.bi-calendar-date-fill::before { + content: ""; } + +.bi-calendar-date::before { + content: ""; } + +.bi-calendar-day-fill::before { + content: ""; } + +.bi-calendar-day::before { + content: ""; } + +.bi-calendar-event-fill::before { + content: ""; } + +.bi-calendar-event::before { + content: ""; } + +.bi-calendar-fill::before { + content: ""; } + +.bi-calendar-minus-fill::before { + content: ""; } + +.bi-calendar-minus::before { + content: ""; } + +.bi-calendar-month-fill::before { + content: ""; } + +.bi-calendar-month::before { + content: ""; } + +.bi-calendar-plus-fill::before { + content: ""; } + +.bi-calendar-plus::before { + content: ""; } + +.bi-calendar-range-fill::before { + content: ""; } + +.bi-calendar-range::before { + content: ""; } + +.bi-calendar-week-fill::before { + content: ""; } + +.bi-calendar-week::before { + content: ""; } + +.bi-calendar-x-fill::before { + content: ""; } + +.bi-calendar-x::before { + content: ""; } + +.bi-calendar::before { + content: ""; } + +.bi-calendar2-check-fill::before { + content: ""; } + +.bi-calendar2-check::before { + content: ""; } + +.bi-calendar2-date-fill::before { + content: ""; } + +.bi-calendar2-date::before { + content: ""; } + +.bi-calendar2-day-fill::before { + content: ""; } + +.bi-calendar2-day::before { + content: ""; } + +.bi-calendar2-event-fill::before { + content: ""; } + +.bi-calendar2-event::before { + content: ""; } + +.bi-calendar2-fill::before { + content: ""; } + +.bi-calendar2-minus-fill::before { + content: ""; } + +.bi-calendar2-minus::before { + content: ""; } + +.bi-calendar2-month-fill::before { + content: ""; } + +.bi-calendar2-month::before { + content: ""; } + +.bi-calendar2-plus-fill::before { + content: ""; } + +.bi-calendar2-plus::before { + content: ""; } + +.bi-calendar2-range-fill::before { + content: ""; } + +.bi-calendar2-range::before { + content: ""; } + +.bi-calendar2-week-fill::before { + content: ""; } + +.bi-calendar2-week::before { + content: ""; } + +.bi-calendar2-x-fill::before { + content: ""; } + +.bi-calendar2-x::before { + content: ""; } + +.bi-calendar2::before { + content: ""; } + +.bi-calendar3-event-fill::before { + content: ""; } + +.bi-calendar3-event::before { + content: ""; } + +.bi-calendar3-fill::before { + content: ""; } + +.bi-calendar3-range-fill::before { + content: ""; } + +.bi-calendar3-range::before { + content: ""; } + +.bi-calendar3-week-fill::before { + content: ""; } + +.bi-calendar3-week::before { + content: ""; } + +.bi-calendar3::before { + content: ""; } + +.bi-calendar4-event::before { + content: ""; } + +.bi-calendar4-range::before { + content: ""; } + +.bi-calendar4-week::before { + content: ""; } + +.bi-calendar4::before { + content: ""; } + +.bi-camera-fill::before { + content: ""; } + +.bi-camera-reels-fill::before { + content: ""; } + +.bi-camera-reels::before { + content: ""; } + +.bi-camera-video-fill::before { + content: ""; } + +.bi-camera-video-off-fill::before { + content: ""; } + +.bi-camera-video-off::before { + content: ""; } + +.bi-camera-video::before { + content: ""; } + +.bi-camera::before { + content: ""; } + +.bi-camera2::before { + content: ""; } + +.bi-capslock-fill::before { + content: ""; } + +.bi-capslock::before { + content: ""; } + +.bi-card-checklist::before { + content: ""; } + +.bi-card-heading::before { + content: ""; } + +.bi-card-image::before { + content: ""; } + +.bi-card-list::before { + content: ""; } + +.bi-card-text::before { + content: ""; } + +.bi-caret-down-fill::before { + content: ""; } + +.bi-caret-down-square-fill::before { + content: ""; } + +.bi-caret-down-square::before { + content: ""; } + +.bi-caret-down::before { + content: ""; } + +.bi-caret-left-fill::before { + content: ""; } + +.bi-caret-left-square-fill::before { + content: ""; } + +.bi-caret-left-square::before { + content: ""; } + +.bi-caret-left::before { + content: ""; } + +.bi-caret-right-fill::before { + content: ""; } + +.bi-caret-right-square-fill::before { + content: ""; } + +.bi-caret-right-square::before { + content: ""; } + +.bi-caret-right::before { + content: ""; } + +.bi-caret-up-fill::before { + content: ""; } + +.bi-caret-up-square-fill::before { + content: ""; } + +.bi-caret-up-square::before { + content: ""; } + +.bi-caret-up::before { + content: ""; } + +.bi-cart-check-fill::before { + content: ""; } + +.bi-cart-check::before { + content: ""; } + +.bi-cart-dash-fill::before { + content: ""; } + +.bi-cart-dash::before { + content: ""; } + +.bi-cart-fill::before { + content: ""; } + +.bi-cart-plus-fill::before { + content: ""; } + +.bi-cart-plus::before { + content: ""; } + +.bi-cart-x-fill::before { + content: ""; } + +.bi-cart-x::before { + content: ""; } + +.bi-cart::before { + content: ""; } + +.bi-cart2::before { + content: ""; } + +.bi-cart3::before { + content: ""; } + +.bi-cart4::before { + content: ""; } + +.bi-cash-stack::before { + content: ""; } + +.bi-cash::before { + content: ""; } + +.bi-cast::before { + content: ""; } + +.bi-chat-dots-fill::before { + content: ""; } + +.bi-chat-dots::before { + content: ""; } + +.bi-chat-fill::before { + content: ""; } + +.bi-chat-left-dots-fill::before { + content: ""; } + +.bi-chat-left-dots::before { + content: ""; } + +.bi-chat-left-fill::before { + content: ""; } + +.bi-chat-left-quote-fill::before { + content: ""; } + +.bi-chat-left-quote::before { + content: ""; } + +.bi-chat-left-text-fill::before { + content: ""; } + +.bi-chat-left-text::before { + content: ""; } + +.bi-chat-left::before { + content: ""; } + +.bi-chat-quote-fill::before { + content: ""; } + +.bi-chat-quote::before { + content: ""; } + +.bi-chat-right-dots-fill::before { + content: ""; } + +.bi-chat-right-dots::before { + content: ""; } + +.bi-chat-right-fill::before { + content: ""; } + +.bi-chat-right-quote-fill::before { + content: ""; } + +.bi-chat-right-quote::before { + content: ""; } + +.bi-chat-right-text-fill::before { + content: ""; } + +.bi-chat-right-text::before { + content: ""; } + +.bi-chat-right::before { + content: ""; } + +.bi-chat-square-dots-fill::before { + content: ""; } + +.bi-chat-square-dots::before { + content: ""; } + +.bi-chat-square-fill::before { + content: ""; } + +.bi-chat-square-quote-fill::before { + content: ""; } + +.bi-chat-square-quote::before { + content: ""; } + +.bi-chat-square-text-fill::before { + content: ""; } + +.bi-chat-square-text::before { + content: ""; } + +.bi-chat-square::before { + content: ""; } + +.bi-chat-text-fill::before { + content: ""; } + +.bi-chat-text::before { + content: ""; } + +.bi-chat::before { + content: ""; } + +.bi-check-all::before { + content: ""; } + +.bi-check-circle-fill::before { + content: ""; } + +.bi-check-circle::before { + content: ""; } + +.bi-check-square-fill::before { + content: ""; } + +.bi-check-square::before { + content: ""; } + +.bi-check::before { + content: ""; } + +.bi-check2-all::before { + content: ""; } + +.bi-check2-circle::before { + content: ""; } + +.bi-check2-square::before { + content: ""; } + +.bi-check2::before { + content: ""; } + +.bi-chevron-bar-contract::before { + content: ""; } + +.bi-chevron-bar-down::before { + content: ""; } + +.bi-chevron-bar-expand::before { + content: ""; } + +.bi-chevron-bar-left::before { + content: ""; } + +.bi-chevron-bar-right::before { + content: ""; } + +.bi-chevron-bar-up::before { + content: ""; } + +.bi-chevron-compact-down::before { + content: ""; } + +.bi-chevron-compact-left::before { + content: ""; } + +.bi-chevron-compact-right::before { + content: ""; } + +.bi-chevron-compact-up::before { + content: ""; } + +.bi-chevron-contract::before { + content: ""; } + +.bi-chevron-double-down::before { + content: ""; } + +.bi-chevron-double-left::before { + content: ""; } + +.bi-chevron-double-right::before { + content: ""; } + +.bi-chevron-double-up::before { + content: ""; } + +.bi-chevron-down::before { + content: ""; } + +.bi-chevron-expand::before { + content: ""; } + +.bi-chevron-left::before { + content: ""; } + +.bi-chevron-right::before { + content: ""; } + +.bi-chevron-up::before { + content: ""; } + +.bi-circle-fill::before { + content: ""; } + +.bi-circle-half::before { + content: ""; } + +.bi-circle-square::before { + content: ""; } + +.bi-circle::before { + content: ""; } + +.bi-clipboard-check::before { + content: ""; } + +.bi-clipboard-data::before { + content: ""; } + +.bi-clipboard-minus::before { + content: ""; } + +.bi-clipboard-plus::before { + content: ""; } + +.bi-clipboard-x::before { + content: ""; } + +.bi-clipboard::before { + content: ""; } + +.bi-clock-fill::before { + content: ""; } + +.bi-clock-history::before { + content: ""; } + +.bi-clock::before { + content: ""; } + +.bi-cloud-arrow-down-fill::before { + content: ""; } + +.bi-cloud-arrow-down::before { + content: ""; } + +.bi-cloud-arrow-up-fill::before { + content: ""; } + +.bi-cloud-arrow-up::before { + content: ""; } + +.bi-cloud-check-fill::before { + content: ""; } + +.bi-cloud-check::before { + content: ""; } + +.bi-cloud-download-fill::before { + content: ""; } + +.bi-cloud-download::before { + content: ""; } + +.bi-cloud-drizzle-fill::before { + content: ""; } + +.bi-cloud-drizzle::before { + content: ""; } + +.bi-cloud-fill::before { + content: ""; } + +.bi-cloud-fog-fill::before { + content: ""; } + +.bi-cloud-fog::before { + content: ""; } + +.bi-cloud-fog2-fill::before { + content: ""; } + +.bi-cloud-fog2::before { + content: ""; } + +.bi-cloud-hail-fill::before { + content: ""; } + +.bi-cloud-hail::before { + content: ""; } + +.bi-cloud-haze-fill::before { + content: ""; } + +.bi-cloud-haze::before { + content: ""; } + +.bi-cloud-haze2-fill::before { + content: ""; } + +.bi-cloud-lightning-fill::before { + content: ""; } + +.bi-cloud-lightning-rain-fill::before { + content: ""; } + +.bi-cloud-lightning-rain::before { + content: ""; } + +.bi-cloud-lightning::before { + content: ""; } + +.bi-cloud-minus-fill::before { + content: ""; } + +.bi-cloud-minus::before { + content: ""; } + +.bi-cloud-moon-fill::before { + content: ""; } + +.bi-cloud-moon::before { + content: ""; } + +.bi-cloud-plus-fill::before { + content: ""; } + +.bi-cloud-plus::before { + content: ""; } + +.bi-cloud-rain-fill::before { + content: ""; } + +.bi-cloud-rain-heavy-fill::before { + content: ""; } + +.bi-cloud-rain-heavy::before { + content: ""; } + +.bi-cloud-rain::before { + content: ""; } + +.bi-cloud-slash-fill::before { + content: ""; } + +.bi-cloud-slash::before { + content: ""; } + +.bi-cloud-sleet-fill::before { + content: ""; } + +.bi-cloud-sleet::before { + content: ""; } + +.bi-cloud-snow-fill::before { + content: ""; } + +.bi-cloud-snow::before { + content: ""; } + +.bi-cloud-sun-fill::before { + content: ""; } + +.bi-cloud-sun::before { + content: ""; } + +.bi-cloud-upload-fill::before { + content: ""; } + +.bi-cloud-upload::before { + content: ""; } + +.bi-cloud::before { + content: ""; } + +.bi-clouds-fill::before { + content: ""; } + +.bi-clouds::before { + content: ""; } + +.bi-cloudy-fill::before { + content: ""; } + +.bi-cloudy::before { + content: ""; } + +.bi-code-slash::before { + content: ""; } + +.bi-code-square::before { + content: ""; } + +.bi-code::before { + content: ""; } + +.bi-collection-fill::before { + content: ""; } + +.bi-collection-play-fill::before { + content: ""; } + +.bi-collection-play::before { + content: ""; } + +.bi-collection::before { + content: ""; } + +.bi-columns-gap::before { + content: ""; } + +.bi-columns::before { + content: ""; } + +.bi-command::before { + content: ""; } + +.bi-compass-fill::before { + content: ""; } + +.bi-compass::before { + content: ""; } + +.bi-cone-striped::before { + content: ""; } + +.bi-cone::before { + content: ""; } + +.bi-controller::before { + content: ""; } + +.bi-cpu-fill::before { + content: ""; } + +.bi-cpu::before { + content: ""; } + +.bi-credit-card-2-back-fill::before { + content: ""; } + +.bi-credit-card-2-back::before { + content: ""; } + +.bi-credit-card-2-front-fill::before { + content: ""; } + +.bi-credit-card-2-front::before { + content: ""; } + +.bi-credit-card-fill::before { + content: ""; } + +.bi-credit-card::before { + content: ""; } + +.bi-crop::before { + content: ""; } + +.bi-cup-fill::before { + content: ""; } + +.bi-cup-straw::before { + content: ""; } + +.bi-cup::before { + content: ""; } + +.bi-cursor-fill::before { + content: ""; } + +.bi-cursor-text::before { + content: ""; } + +.bi-cursor::before { + content: ""; } + +.bi-dash-circle-dotted::before { + content: ""; } + +.bi-dash-circle-fill::before { + content: ""; } + +.bi-dash-circle::before { + content: ""; } + +.bi-dash-square-dotted::before { + content: ""; } + +.bi-dash-square-fill::before { + content: ""; } + +.bi-dash-square::before { + content: ""; } + +.bi-dash::before { + content: ""; } + +.bi-diagram-2-fill::before { + content: ""; } + +.bi-diagram-2::before { + content: ""; } + +.bi-diagram-3-fill::before { + content: ""; } + +.bi-diagram-3::before { + content: ""; } + +.bi-diamond-fill::before { + content: ""; } + +.bi-diamond-half::before { + content: ""; } + +.bi-diamond::before { + content: ""; } + +.bi-dice-1-fill::before { + content: ""; } + +.bi-dice-1::before { + content: ""; } + +.bi-dice-2-fill::before { + content: ""; } + +.bi-dice-2::before { + content: ""; } + +.bi-dice-3-fill::before { + content: ""; } + +.bi-dice-3::before { + content: ""; } + +.bi-dice-4-fill::before { + content: ""; } + +.bi-dice-4::before { + content: ""; } + +.bi-dice-5-fill::before { + content: ""; } + +.bi-dice-5::before { + content: ""; } + +.bi-dice-6-fill::before { + content: ""; } + +.bi-dice-6::before { + content: ""; } + +.bi-disc-fill::before { + content: ""; } + +.bi-disc::before { + content: ""; } + +.bi-discord::before { + content: ""; } + +.bi-display-fill::before { + content: ""; } + +.bi-display::before { + content: ""; } + +.bi-distribute-horizontal::before { + content: ""; } + +.bi-distribute-vertical::before { + content: ""; } + +.bi-door-closed-fill::before { + content: ""; } + +.bi-door-closed::before { + content: ""; } + +.bi-door-open-fill::before { + content: ""; } + +.bi-door-open::before { + content: ""; } + +.bi-dot::before { + content: ""; } + +.bi-download::before { + content: ""; } + +.bi-droplet-fill::before { + content: ""; } + +.bi-droplet-half::before { + content: ""; } + +.bi-droplet::before { + content: ""; } + +.bi-earbuds::before { + content: ""; } + +.bi-easel-fill::before { + content: ""; } + +.bi-easel::before { + content: ""; } + +.bi-egg-fill::before { + content: ""; } + +.bi-egg-fried::before { + content: ""; } + +.bi-egg::before { + content: ""; } + +.bi-eject-fill::before { + content: ""; } + +.bi-eject::before { + content: ""; } + +.bi-emoji-angry-fill::before { + content: ""; } + +.bi-emoji-angry::before { + content: ""; } + +.bi-emoji-dizzy-fill::before { + content: ""; } + +.bi-emoji-dizzy::before { + content: ""; } + +.bi-emoji-expressionless-fill::before { + content: ""; } + +.bi-emoji-expressionless::before { + content: ""; } + +.bi-emoji-frown-fill::before { + content: ""; } + +.bi-emoji-frown::before { + content: ""; } + +.bi-emoji-heart-eyes-fill::before { + content: ""; } + +.bi-emoji-heart-eyes::before { + content: ""; } + +.bi-emoji-laughing-fill::before { + content: ""; } + +.bi-emoji-laughing::before { + content: ""; } + +.bi-emoji-neutral-fill::before { + content: ""; } + +.bi-emoji-neutral::before { + content: ""; } + +.bi-emoji-smile-fill::before { + content: ""; } + +.bi-emoji-smile-upside-down-fill::before { + content: ""; } + +.bi-emoji-smile-upside-down::before { + content: ""; } + +.bi-emoji-smile::before { + content: ""; } + +.bi-emoji-sunglasses-fill::before { + content: ""; } + +.bi-emoji-sunglasses::before { + content: ""; } + +.bi-emoji-wink-fill::before { + content: ""; } + +.bi-emoji-wink::before { + content: ""; } + +.bi-envelope-fill::before { + content: ""; } + +.bi-envelope-open-fill::before { + content: ""; } + +.bi-envelope-open::before { + content: ""; } + +.bi-envelope::before { + content: ""; } + +.bi-eraser-fill::before { + content: ""; } + +.bi-eraser::before { + content: ""; } + +.bi-exclamation-circle-fill::before { + content: ""; } + +.bi-exclamation-circle::before { + content: ""; } + +.bi-exclamation-diamond-fill::before { + content: ""; } + +.bi-exclamation-diamond::before { + content: ""; } + +.bi-exclamation-octagon-fill::before { + content: ""; } + +.bi-exclamation-octagon::before { + content: ""; } + +.bi-exclamation-square-fill::before { + content: ""; } + +.bi-exclamation-square::before { + content: ""; } + +.bi-exclamation-triangle-fill::before { + content: ""; } + +.bi-exclamation-triangle::before { + content: ""; } + +.bi-exclamation::before { + content: ""; } + +.bi-exclude::before { + content: ""; } + +.bi-eye-fill::before { + content: ""; } + +.bi-eye-slash-fill::before { + content: ""; } + +.bi-eye-slash::before { + content: ""; } + +.bi-eye::before { + content: ""; } + +.bi-eyedropper::before { + content: ""; } + +.bi-eyeglasses::before { + content: ""; } + +.bi-facebook::before { + content: ""; } + +.bi-file-arrow-down-fill::before { + content: ""; } + +.bi-file-arrow-down::before { + content: ""; } + +.bi-file-arrow-up-fill::before { + content: ""; } + +.bi-file-arrow-up::before { + content: ""; } + +.bi-file-bar-graph-fill::before { + content: ""; } + +.bi-file-bar-graph::before { + content: ""; } + +.bi-file-binary-fill::before { + content: ""; } + +.bi-file-binary::before { + content: ""; } + +.bi-file-break-fill::before { + content: ""; } + +.bi-file-break::before { + content: ""; } + +.bi-file-check-fill::before { + content: ""; } + +.bi-file-check::before { + content: ""; } + +.bi-file-code-fill::before { + content: ""; } + +.bi-file-code::before { + content: ""; } + +.bi-file-diff-fill::before { + content: ""; } + +.bi-file-diff::before { + content: ""; } + +.bi-file-earmark-arrow-down-fill::before { + content: ""; } + +.bi-file-earmark-arrow-down::before { + content: ""; } + +.bi-file-earmark-arrow-up-fill::before { + content: ""; } + +.bi-file-earmark-arrow-up::before { + content: ""; } + +.bi-file-earmark-bar-graph-fill::before { + content: ""; } + +.bi-file-earmark-bar-graph::before { + content: ""; } + +.bi-file-earmark-binary-fill::before { + content: ""; } + +.bi-file-earmark-binary::before { + content: ""; } + +.bi-file-earmark-break-fill::before { + content: ""; } + +.bi-file-earmark-break::before { + content: ""; } + +.bi-file-earmark-check-fill::before { + content: ""; } + +.bi-file-earmark-check::before { + content: ""; } + +.bi-file-earmark-code-fill::before { + content: ""; } + +.bi-file-earmark-code::before { + content: ""; } + +.bi-file-earmark-diff-fill::before { + content: ""; } + +.bi-file-earmark-diff::before { + content: ""; } + +.bi-file-earmark-easel-fill::before { + content: ""; } + +.bi-file-earmark-easel::before { + content: ""; } + +.bi-file-earmark-excel-fill::before { + content: ""; } + +.bi-file-earmark-excel::before { + content: ""; } + +.bi-file-earmark-fill::before { + content: ""; } + +.bi-file-earmark-font-fill::before { + content: ""; } + +.bi-file-earmark-font::before { + content: ""; } + +.bi-file-earmark-image-fill::before { + content: ""; } + +.bi-file-earmark-image::before { + content: ""; } + +.bi-file-earmark-lock-fill::before { + content: ""; } + +.bi-file-earmark-lock::before { + content: ""; } + +.bi-file-earmark-lock2-fill::before { + content: ""; } + +.bi-file-earmark-lock2::before { + content: ""; } + +.bi-file-earmark-medical-fill::before { + content: ""; } + +.bi-file-earmark-medical::before { + content: ""; } + +.bi-file-earmark-minus-fill::before { + content: ""; } + +.bi-file-earmark-minus::before { + content: ""; } + +.bi-file-earmark-music-fill::before { + content: ""; } + +.bi-file-earmark-music::before { + content: ""; } + +.bi-file-earmark-person-fill::before { + content: ""; } + +.bi-file-earmark-person::before { + content: ""; } + +.bi-file-earmark-play-fill::before { + content: ""; } + +.bi-file-earmark-play::before { + content: ""; } + +.bi-file-earmark-plus-fill::before { + content: ""; } + +.bi-file-earmark-plus::before { + content: ""; } + +.bi-file-earmark-post-fill::before { + content: ""; } + +.bi-file-earmark-post::before { + content: ""; } + +.bi-file-earmark-ppt-fill::before { + content: ""; } + +.bi-file-earmark-ppt::before { + content: ""; } + +.bi-file-earmark-richtext-fill::before { + content: ""; } + +.bi-file-earmark-richtext::before { + content: ""; } + +.bi-file-earmark-ruled-fill::before { + content: ""; } + +.bi-file-earmark-ruled::before { + content: ""; } + +.bi-file-earmark-slides-fill::before { + content: ""; } + +.bi-file-earmark-slides::before { + content: ""; } + +.bi-file-earmark-spreadsheet-fill::before { + content: ""; } + +.bi-file-earmark-spreadsheet::before { + content: ""; } + +.bi-file-earmark-text-fill::before { + content: ""; } + +.bi-file-earmark-text::before { + content: ""; } + +.bi-file-earmark-word-fill::before { + content: ""; } + +.bi-file-earmark-word::before { + content: ""; } + +.bi-file-earmark-x-fill::before { + content: ""; } + +.bi-file-earmark-x::before { + content: ""; } + +.bi-file-earmark-zip-fill::before { + content: ""; } + +.bi-file-earmark-zip::before { + content: ""; } + +.bi-file-earmark::before { + content: ""; } + +.bi-file-easel-fill::before { + content: ""; } + +.bi-file-easel::before { + content: ""; } + +.bi-file-excel-fill::before { + content: ""; } + +.bi-file-excel::before { + content: ""; } + +.bi-file-fill::before { + content: ""; } + +.bi-file-font-fill::before { + content: ""; } + +.bi-file-font::before { + content: ""; } + +.bi-file-image-fill::before { + content: ""; } + +.bi-file-image::before { + content: ""; } + +.bi-file-lock-fill::before { + content: ""; } + +.bi-file-lock::before { + content: ""; } + +.bi-file-lock2-fill::before { + content: ""; } + +.bi-file-lock2::before { + content: ""; } + +.bi-file-medical-fill::before { + content: ""; } + +.bi-file-medical::before { + content: ""; } + +.bi-file-minus-fill::before { + content: ""; } + +.bi-file-minus::before { + content: ""; } + +.bi-file-music-fill::before { + content: ""; } + +.bi-file-music::before { + content: ""; } + +.bi-file-person-fill::before { + content: ""; } + +.bi-file-person::before { + content: ""; } + +.bi-file-play-fill::before { + content: ""; } + +.bi-file-play::before { + content: ""; } + +.bi-file-plus-fill::before { + content: ""; } + +.bi-file-plus::before { + content: ""; } + +.bi-file-post-fill::before { + content: ""; } + +.bi-file-post::before { + content: ""; } + +.bi-file-ppt-fill::before { + content: ""; } + +.bi-file-ppt::before { + content: ""; } + +.bi-file-richtext-fill::before { + content: ""; } + +.bi-file-richtext::before { + content: ""; } + +.bi-file-ruled-fill::before { + content: ""; } + +.bi-file-ruled::before { + content: ""; } + +.bi-file-slides-fill::before { + content: ""; } + +.bi-file-slides::before { + content: ""; } + +.bi-file-spreadsheet-fill::before { + content: ""; } + +.bi-file-spreadsheet::before { + content: ""; } + +.bi-file-text-fill::before { + content: ""; } + +.bi-file-text::before { + content: ""; } + +.bi-file-word-fill::before { + content: ""; } + +.bi-file-word::before { + content: ""; } + +.bi-file-x-fill::before { + content: ""; } + +.bi-file-x::before { + content: ""; } + +.bi-file-zip-fill::before { + content: ""; } + +.bi-file-zip::before { + content: ""; } + +.bi-file::before { + content: ""; } + +.bi-files-alt::before { + content: ""; } + +.bi-files::before { + content: ""; } + +.bi-film::before { + content: ""; } + +.bi-filter-circle-fill::before { + content: ""; } + +.bi-filter-circle::before { + content: ""; } + +.bi-filter-left::before { + content: ""; } + +.bi-filter-right::before { + content: ""; } + +.bi-filter-square-fill::before { + content: ""; } + +.bi-filter-square::before { + content: ""; } + +.bi-filter::before { + content: ""; } + +.bi-flag-fill::before { + content: ""; } + +.bi-flag::before { + content: ""; } + +.bi-flower1::before { + content: ""; } + +.bi-flower2::before { + content: ""; } + +.bi-flower3::before { + content: ""; } + +.bi-folder-check::before { + content: ""; } + +.bi-folder-fill::before { + content: ""; } + +.bi-folder-minus::before { + content: ""; } + +.bi-folder-plus::before { + content: ""; } + +.bi-folder-symlink-fill::before { + content: ""; } + +.bi-folder-symlink::before { + content: ""; } + +.bi-folder-x::before { + content: ""; } + +.bi-folder::before { + content: ""; } + +.bi-folder2-open::before { + content: ""; } + +.bi-folder2::before { + content: ""; } + +.bi-fonts::before { + content: ""; } + +.bi-forward-fill::before { + content: ""; } + +.bi-forward::before { + content: ""; } + +.bi-front::before { + content: ""; } + +.bi-fullscreen-exit::before { + content: ""; } + +.bi-fullscreen::before { + content: ""; } + +.bi-funnel-fill::before { + content: ""; } + +.bi-funnel::before { + content: ""; } + +.bi-gear-fill::before { + content: ""; } + +.bi-gear-wide-connected::before { + content: ""; } + +.bi-gear-wide::before { + content: ""; } + +.bi-gear::before { + content: ""; } + +.bi-gem::before { + content: ""; } + +.bi-geo-alt-fill::before { + content: ""; } + +.bi-geo-alt::before { + content: ""; } + +.bi-geo-fill::before { + content: ""; } + +.bi-geo::before { + content: ""; } + +.bi-gift-fill::before { + content: ""; } + +.bi-gift::before { + content: ""; } + +.bi-github::before { + content: ""; } + +.bi-globe::before { + content: ""; } + +.bi-globe2::before { + content: ""; } + +.bi-google::before { + content: ""; } + +.bi-graph-down::before { + content: ""; } + +.bi-graph-up::before { + content: ""; } + +.bi-grid-1x2-fill::before { + content: ""; } + +.bi-grid-1x2::before { + content: ""; } + +.bi-grid-3x2-gap-fill::before { + content: ""; } + +.bi-grid-3x2-gap::before { + content: ""; } + +.bi-grid-3x2::before { + content: ""; } + +.bi-grid-3x3-gap-fill::before { + content: ""; } + +.bi-grid-3x3-gap::before { + content: ""; } + +.bi-grid-3x3::before { + content: ""; } + +.bi-grid-fill::before { + content: ""; } + +.bi-grid::before { + content: ""; } + +.bi-grip-horizontal::before { + content: ""; } + +.bi-grip-vertical::before { + content: ""; } + +.bi-hammer::before { + content: ""; } + +.bi-hand-index-fill::before { + content: ""; } + +.bi-hand-index-thumb-fill::before { + content: ""; } + +.bi-hand-index-thumb::before { + content: ""; } + +.bi-hand-index::before { + content: ""; } + +.bi-hand-thumbs-down-fill::before { + content: ""; } + +.bi-hand-thumbs-down::before { + content: ""; } + +.bi-hand-thumbs-up-fill::before { + content: ""; } + +.bi-hand-thumbs-up::before { + content: ""; } + +.bi-handbag-fill::before { + content: ""; } + +.bi-handbag::before { + content: ""; } + +.bi-hash::before { + content: ""; } + +.bi-hdd-fill::before { + content: ""; } + +.bi-hdd-network-fill::before { + content: ""; } + +.bi-hdd-network::before { + content: ""; } + +.bi-hdd-rack-fill::before { + content: ""; } + +.bi-hdd-rack::before { + content: ""; } + +.bi-hdd-stack-fill::before { + content: ""; } + +.bi-hdd-stack::before { + content: ""; } + +.bi-hdd::before { + content: ""; } + +.bi-headphones::before { + content: ""; } + +.bi-headset::before { + content: ""; } + +.bi-heart-fill::before { + content: ""; } + +.bi-heart-half::before { + content: ""; } + +.bi-heart::before { + content: ""; } + +.bi-heptagon-fill::before { + content: ""; } + +.bi-heptagon-half::before { + content: ""; } + +.bi-heptagon::before { + content: ""; } + +.bi-hexagon-fill::before { + content: ""; } + +.bi-hexagon-half::before { + content: ""; } + +.bi-hexagon::before { + content: ""; } + +.bi-hourglass-bottom::before { + content: ""; } + +.bi-hourglass-split::before { + content: ""; } + +.bi-hourglass-top::before { + content: ""; } + +.bi-hourglass::before { + content: ""; } + +.bi-house-door-fill::before { + content: ""; } + +.bi-house-door::before { + content: ""; } + +.bi-house-fill::before { + content: ""; } + +.bi-house::before { + content: ""; } + +.bi-hr::before { + content: ""; } + +.bi-hurricane::before { + content: ""; } + +.bi-image-alt::before { + content: ""; } + +.bi-image-fill::before { + content: ""; } + +.bi-image::before { + content: ""; } + +.bi-images::before { + content: ""; } + +.bi-inbox-fill::before { + content: ""; } + +.bi-inbox::before { + content: ""; } + +.bi-inboxes-fill::before { + content: ""; } + +.bi-inboxes::before { + content: ""; } + +.bi-info-circle-fill::before { + content: ""; } + +.bi-info-circle::before { + content: ""; } + +.bi-info-square-fill::before { + content: ""; } + +.bi-info-square::before { + content: ""; } + +.bi-info::before { + content: ""; } + +.bi-input-cursor-text::before { + content: ""; } + +.bi-input-cursor::before { + content: ""; } + +.bi-instagram::before { + content: ""; } + +.bi-intersect::before { + content: ""; } + +.bi-journal-album::before { + content: ""; } + +.bi-journal-arrow-down::before { + content: ""; } + +.bi-journal-arrow-up::before { + content: ""; } + +.bi-journal-bookmark-fill::before { + content: ""; } + +.bi-journal-bookmark::before { + content: ""; } + +.bi-journal-check::before { + content: ""; } + +.bi-journal-code::before { + content: ""; } + +.bi-journal-medical::before { + content: ""; } + +.bi-journal-minus::before { + content: ""; } + +.bi-journal-plus::before { + content: ""; } + +.bi-journal-richtext::before { + content: ""; } + +.bi-journal-text::before { + content: ""; } + +.bi-journal-x::before { + content: ""; } + +.bi-journal::before { + content: ""; } + +.bi-journals::before { + content: ""; } + +.bi-joystick::before { + content: ""; } + +.bi-justify-left::before { + content: ""; } + +.bi-justify-right::before { + content: ""; } + +.bi-justify::before { + content: ""; } + +.bi-kanban-fill::before { + content: ""; } + +.bi-kanban::before { + content: ""; } + +.bi-key-fill::before { + content: ""; } + +.bi-key::before { + content: ""; } + +.bi-keyboard-fill::before { + content: ""; } + +.bi-keyboard::before { + content: ""; } + +.bi-ladder::before { + content: ""; } + +.bi-lamp-fill::before { + content: ""; } + +.bi-lamp::before { + content: ""; } + +.bi-laptop-fill::before { + content: ""; } + +.bi-laptop::before { + content: ""; } + +.bi-layer-backward::before { + content: ""; } + +.bi-layer-forward::before { + content: ""; } + +.bi-layers-fill::before { + content: ""; } + +.bi-layers-half::before { + content: ""; } + +.bi-layers::before { + content: ""; } + +.bi-layout-sidebar-inset-reverse::before { + content: ""; } + +.bi-layout-sidebar-inset::before { + content: ""; } + +.bi-layout-sidebar-reverse::before { + content: ""; } + +.bi-layout-sidebar::before { + content: ""; } + +.bi-layout-split::before { + content: ""; } + +.bi-layout-text-sidebar-reverse::before { + content: ""; } + +.bi-layout-text-sidebar::before { + content: ""; } + +.bi-layout-text-window-reverse::before { + content: ""; } + +.bi-layout-text-window::before { + content: ""; } + +.bi-layout-three-columns::before { + content: ""; } + +.bi-layout-wtf::before { + content: ""; } + +.bi-life-preserver::before { + content: ""; } + +.bi-lightbulb-fill::before { + content: ""; } + +.bi-lightbulb-off-fill::before { + content: ""; } + +.bi-lightbulb-off::before { + content: ""; } + +.bi-lightbulb::before { + content: ""; } + +.bi-lightning-charge-fill::before { + content: ""; } + +.bi-lightning-charge::before { + content: ""; } + +.bi-lightning-fill::before { + content: ""; } + +.bi-lightning::before { + content: ""; } + +.bi-link-45deg::before { + content: ""; } + +.bi-link::before { + content: ""; } + +.bi-linkedin::before { + content: ""; } + +.bi-list-check::before { + content: ""; } + +.bi-list-nested::before { + content: ""; } + +.bi-list-ol::before { + content: ""; } + +.bi-list-stars::before { + content: ""; } + +.bi-list-task::before { + content: ""; } + +.bi-list-ul::before { + content: ""; } + +.bi-list::before { + content: ""; } + +.bi-lock-fill::before { + content: ""; } + +.bi-lock::before { + content: ""; } + +.bi-mailbox::before { + content: ""; } + +.bi-mailbox2::before { + content: ""; } + +.bi-map-fill::before { + content: ""; } + +.bi-map::before { + content: ""; } + +.bi-markdown-fill::before { + content: ""; } + +.bi-markdown::before { + content: ""; } + +.bi-mask::before { + content: ""; } + +.bi-megaphone-fill::before { + content: ""; } + +.bi-megaphone::before { + content: ""; } + +.bi-menu-app-fill::before { + content: ""; } + +.bi-menu-app::before { + content: ""; } + +.bi-menu-button-fill::before { + content: ""; } + +.bi-menu-button-wide-fill::before { + content: ""; } + +.bi-menu-button-wide::before { + content: ""; } + +.bi-menu-button::before { + content: ""; } + +.bi-menu-down::before { + content: ""; } + +.bi-menu-up::before { + content: ""; } + +.bi-mic-fill::before { + content: ""; } + +.bi-mic-mute-fill::before { + content: ""; } + +.bi-mic-mute::before { + content: ""; } + +.bi-mic::before { + content: ""; } + +.bi-minecart-loaded::before { + content: ""; } + +.bi-minecart::before { + content: ""; } + +.bi-moisture::before { + content: ""; } + +.bi-moon-fill::before { + content: ""; } + +.bi-moon-stars-fill::before { + content: ""; } + +.bi-moon-stars::before { + content: ""; } + +.bi-moon::before { + content: ""; } + +.bi-mouse-fill::before { + content: ""; } + +.bi-mouse::before { + content: ""; } + +.bi-mouse2-fill::before { + content: ""; } + +.bi-mouse2::before { + content: ""; } + +.bi-mouse3-fill::before { + content: ""; } + +.bi-mouse3::before { + content: ""; } + +.bi-music-note-beamed::before { + content: ""; } + +.bi-music-note-list::before { + content: ""; } + +.bi-music-note::before { + content: ""; } + +.bi-music-player-fill::before { + content: ""; } + +.bi-music-player::before { + content: ""; } + +.bi-newspaper::before { + content: ""; } + +.bi-node-minus-fill::before { + content: ""; } + +.bi-node-minus::before { + content: ""; } + +.bi-node-plus-fill::before { + content: ""; } + +.bi-node-plus::before { + content: ""; } + +.bi-nut-fill::before { + content: ""; } + +.bi-nut::before { + content: ""; } + +.bi-octagon-fill::before { + content: ""; } + +.bi-octagon-half::before { + content: ""; } + +.bi-octagon::before { + content: ""; } + +.bi-option::before { + content: ""; } + +.bi-outlet::before { + content: ""; } + +.bi-paint-bucket::before { + content: ""; } + +.bi-palette-fill::before { + content: ""; } + +.bi-palette::before { + content: ""; } + +.bi-palette2::before { + content: ""; } + +.bi-paperclip::before { + content: ""; } + +.bi-paragraph::before { + content: ""; } + +.bi-patch-check-fill::before { + content: ""; } + +.bi-patch-check::before { + content: ""; } + +.bi-patch-exclamation-fill::before { + content: ""; } + +.bi-patch-exclamation::before { + content: ""; } + +.bi-patch-minus-fill::before { + content: ""; } + +.bi-patch-minus::before { + content: ""; } + +.bi-patch-plus-fill::before { + content: ""; } + +.bi-patch-plus::before { + content: ""; } + +.bi-patch-question-fill::before { + content: ""; } + +.bi-patch-question::before { + content: ""; } + +.bi-pause-btn-fill::before { + content: ""; } + +.bi-pause-btn::before { + content: ""; } + +.bi-pause-circle-fill::before { + content: ""; } + +.bi-pause-circle::before { + content: ""; } + +.bi-pause-fill::before { + content: ""; } + +.bi-pause::before { + content: ""; } + +.bi-peace-fill::before { + content: ""; } + +.bi-peace::before { + content: ""; } + +.bi-pen-fill::before { + content: ""; } + +.bi-pen::before { + content: ""; } + +.bi-pencil-fill::before { + content: ""; } + +.bi-pencil-square::before { + content: ""; } + +.bi-pencil::before { + content: ""; } + +.bi-pentagon-fill::before { + content: ""; } + +.bi-pentagon-half::before { + content: ""; } + +.bi-pentagon::before { + content: ""; } + +.bi-people-fill::before { + content: ""; } + +.bi-people::before { + content: ""; } + +.bi-percent::before { + content: ""; } + +.bi-person-badge-fill::before { + content: ""; } + +.bi-person-badge::before { + content: ""; } + +.bi-person-bounding-box::before { + content: ""; } + +.bi-person-check-fill::before { + content: ""; } + +.bi-person-check::before { + content: ""; } + +.bi-person-circle::before { + content: ""; } + +.bi-person-dash-fill::before { + content: ""; } + +.bi-person-dash::before { + content: ""; } + +.bi-person-fill::before { + content: ""; } + +.bi-person-lines-fill::before { + content: ""; } + +.bi-person-plus-fill::before { + content: ""; } + +.bi-person-plus::before { + content: ""; } + +.bi-person-square::before { + content: ""; } + +.bi-person-x-fill::before { + content: ""; } + +.bi-person-x::before { + content: ""; } + +.bi-person::before { + content: ""; } + +.bi-phone-fill::before { + content: ""; } + +.bi-phone-landscape-fill::before { + content: ""; } + +.bi-phone-landscape::before { + content: ""; } + +.bi-phone-vibrate-fill::before { + content: ""; } + +.bi-phone-vibrate::before { + content: ""; } + +.bi-phone::before { + content: ""; } + +.bi-pie-chart-fill::before { + content: ""; } + +.bi-pie-chart::before { + content: ""; } + +.bi-pin-angle-fill::before { + content: ""; } + +.bi-pin-angle::before { + content: ""; } + +.bi-pin-fill::before { + content: ""; } + +.bi-pin::before { + content: ""; } + +.bi-pip-fill::before { + content: ""; } + +.bi-pip::before { + content: ""; } + +.bi-play-btn-fill::before { + content: ""; } + +.bi-play-btn::before { + content: ""; } + +.bi-play-circle-fill::before { + content: ""; } + +.bi-play-circle::before { + content: ""; } + +.bi-play-fill::before { + content: ""; } + +.bi-play::before { + content: ""; } + +.bi-plug-fill::before { + content: ""; } + +.bi-plug::before { + content: ""; } + +.bi-plus-circle-dotted::before { + content: ""; } + +.bi-plus-circle-fill::before { + content: ""; } + +.bi-plus-circle::before { + content: ""; } + +.bi-plus-square-dotted::before { + content: ""; } + +.bi-plus-square-fill::before { + content: ""; } + +.bi-plus-square::before { + content: ""; } + +.bi-plus::before { + content: ""; } + +.bi-power::before { + content: ""; } + +.bi-printer-fill::before { + content: ""; } + +.bi-printer::before { + content: ""; } + +.bi-puzzle-fill::before { + content: ""; } + +.bi-puzzle::before { + content: ""; } + +.bi-question-circle-fill::before { + content: ""; } + +.bi-question-circle::before { + content: ""; } + +.bi-question-diamond-fill::before { + content: ""; } + +.bi-question-diamond::before { + content: ""; } + +.bi-question-octagon-fill::before { + content: ""; } + +.bi-question-octagon::before { + content: ""; } + +.bi-question-square-fill::before { + content: ""; } + +.bi-question-square::before { + content: ""; } + +.bi-question::before { + content: ""; } + +.bi-rainbow::before { + content: ""; } + +.bi-receipt-cutoff::before { + content: ""; } + +.bi-receipt::before { + content: ""; } + +.bi-reception-0::before { + content: ""; } + +.bi-reception-1::before { + content: ""; } + +.bi-reception-2::before { + content: ""; } + +.bi-reception-3::before { + content: ""; } + +.bi-reception-4::before { + content: ""; } + +.bi-record-btn-fill::before { + content: ""; } + +.bi-record-btn::before { + content: ""; } + +.bi-record-circle-fill::before { + content: ""; } + +.bi-record-circle::before { + content: ""; } + +.bi-record-fill::before { + content: ""; } + +.bi-record::before { + content: ""; } + +.bi-record2-fill::before { + content: ""; } + +.bi-record2::before { + content: ""; } + +.bi-reply-all-fill::before { + content: ""; } + +.bi-reply-all::before { + content: ""; } + +.bi-reply-fill::before { + content: ""; } + +.bi-reply::before { + content: ""; } + +.bi-rss-fill::before { + content: ""; } + +.bi-rss::before { + content: ""; } + +.bi-rulers::before { + content: ""; } + +.bi-save-fill::before { + content: ""; } + +.bi-save::before { + content: ""; } + +.bi-save2-fill::before { + content: ""; } + +.bi-save2::before { + content: ""; } + +.bi-scissors::before { + content: ""; } + +.bi-screwdriver::before { + content: ""; } + +.bi-search::before { + content: ""; } + +.bi-segmented-nav::before { + content: ""; } + +.bi-server::before { + content: ""; } + +.bi-share-fill::before { + content: ""; } + +.bi-share::before { + content: ""; } + +.bi-shield-check::before { + content: ""; } + +.bi-shield-exclamation::before { + content: ""; } + +.bi-shield-fill-check::before { + content: ""; } + +.bi-shield-fill-exclamation::before { + content: ""; } + +.bi-shield-fill-minus::before { + content: ""; } + +.bi-shield-fill-plus::before { + content: ""; } + +.bi-shield-fill-x::before { + content: ""; } + +.bi-shield-fill::before { + content: ""; } + +.bi-shield-lock-fill::before { + content: ""; } + +.bi-shield-lock::before { + content: ""; } + +.bi-shield-minus::before { + content: ""; } + +.bi-shield-plus::before { + content: ""; } + +.bi-shield-shaded::before { + content: ""; } + +.bi-shield-slash-fill::before { + content: ""; } + +.bi-shield-slash::before { + content: ""; } + +.bi-shield-x::before { + content: ""; } + +.bi-shield::before { + content: ""; } + +.bi-shift-fill::before { + content: ""; } + +.bi-shift::before { + content: ""; } + +.bi-shop-window::before { + content: ""; } + +.bi-shop::before { + content: ""; } + +.bi-shuffle::before { + content: ""; } + +.bi-signpost-2-fill::before { + content: ""; } + +.bi-signpost-2::before { + content: ""; } + +.bi-signpost-fill::before { + content: ""; } + +.bi-signpost-split-fill::before { + content: ""; } + +.bi-signpost-split::before { + content: ""; } + +.bi-signpost::before { + content: ""; } + +.bi-sim-fill::before { + content: ""; } + +.bi-sim::before { + content: ""; } + +.bi-skip-backward-btn-fill::before { + content: ""; } + +.bi-skip-backward-btn::before { + content: ""; } + +.bi-skip-backward-circle-fill::before { + content: ""; } + +.bi-skip-backward-circle::before { + content: ""; } + +.bi-skip-backward-fill::before { + content: ""; } + +.bi-skip-backward::before { + content: ""; } + +.bi-skip-end-btn-fill::before { + content: ""; } + +.bi-skip-end-btn::before { + content: ""; } + +.bi-skip-end-circle-fill::before { + content: ""; } + +.bi-skip-end-circle::before { + content: ""; } + +.bi-skip-end-fill::before { + content: ""; } + +.bi-skip-end::before { + content: ""; } + +.bi-skip-forward-btn-fill::before { + content: ""; } + +.bi-skip-forward-btn::before { + content: ""; } + +.bi-skip-forward-circle-fill::before { + content: ""; } + +.bi-skip-forward-circle::before { + content: ""; } + +.bi-skip-forward-fill::before { + content: ""; } + +.bi-skip-forward::before { + content: ""; } + +.bi-skip-start-btn-fill::before { + content: ""; } + +.bi-skip-start-btn::before { + content: ""; } + +.bi-skip-start-circle-fill::before { + content: ""; } + +.bi-skip-start-circle::before { + content: ""; } + +.bi-skip-start-fill::before { + content: ""; } + +.bi-skip-start::before { + content: ""; } + +.bi-slack::before { + content: ""; } + +.bi-slash-circle-fill::before { + content: ""; } + +.bi-slash-circle::before { + content: ""; } + +.bi-slash-square-fill::before { + content: ""; } + +.bi-slash-square::before { + content: ""; } + +.bi-slash::before { + content: ""; } + +.bi-sliders::before { + content: ""; } + +.bi-smartwatch::before { + content: ""; } + +.bi-snow::before { + content: ""; } + +.bi-snow2::before { + content: ""; } + +.bi-snow3::before { + content: ""; } + +.bi-sort-alpha-down-alt::before { + content: ""; } + +.bi-sort-alpha-down::before { + content: ""; } + +.bi-sort-alpha-up-alt::before { + content: ""; } + +.bi-sort-alpha-up::before { + content: ""; } + +.bi-sort-down-alt::before { + content: ""; } + +.bi-sort-down::before { + content: ""; } + +.bi-sort-numeric-down-alt::before { + content: ""; } + +.bi-sort-numeric-down::before { + content: ""; } + +.bi-sort-numeric-up-alt::before { + content: ""; } + +.bi-sort-numeric-up::before { + content: ""; } + +.bi-sort-up-alt::before { + content: ""; } + +.bi-sort-up::before { + content: ""; } + +.bi-soundwave::before { + content: ""; } + +.bi-speaker-fill::before { + content: ""; } + +.bi-speaker::before { + content: ""; } + +.bi-speedometer::before { + content: ""; } + +.bi-speedometer2::before { + content: ""; } + +.bi-spellcheck::before { + content: ""; } + +.bi-square-fill::before { + content: ""; } + +.bi-square-half::before { + content: ""; } + +.bi-square::before { + content: ""; } + +.bi-stack::before { + content: ""; } + +.bi-star-fill::before { + content: ""; } + +.bi-star-half::before { + content: ""; } + +.bi-star::before { + content: ""; } + +.bi-stars::before { + content: ""; } + +.bi-stickies-fill::before { + content: ""; } + +.bi-stickies::before { + content: ""; } + +.bi-sticky-fill::before { + content: ""; } + +.bi-sticky::before { + content: ""; } + +.bi-stop-btn-fill::before { + content: ""; } + +.bi-stop-btn::before { + content: ""; } + +.bi-stop-circle-fill::before { + content: ""; } + +.bi-stop-circle::before { + content: ""; } + +.bi-stop-fill::before { + content: ""; } + +.bi-stop::before { + content: ""; } + +.bi-stoplights-fill::before { + content: ""; } + +.bi-stoplights::before { + content: ""; } + +.bi-stopwatch-fill::before { + content: ""; } + +.bi-stopwatch::before { + content: ""; } + +.bi-subtract::before { + content: ""; } + +.bi-suit-club-fill::before { + content: ""; } + +.bi-suit-club::before { + content: ""; } + +.bi-suit-diamond-fill::before { + content: ""; } + +.bi-suit-diamond::before { + content: ""; } + +.bi-suit-heart-fill::before { + content: ""; } + +.bi-suit-heart::before { + content: ""; } + +.bi-suit-spade-fill::before { + content: ""; } + +.bi-suit-spade::before { + content: ""; } + +.bi-sun-fill::before { + content: ""; } + +.bi-sun::before { + content: ""; } + +.bi-sunglasses::before { + content: ""; } + +.bi-sunrise-fill::before { + content: ""; } + +.bi-sunrise::before { + content: ""; } + +.bi-sunset-fill::before { + content: ""; } + +.bi-sunset::before { + content: ""; } + +.bi-symmetry-horizontal::before { + content: ""; } + +.bi-symmetry-vertical::before { + content: ""; } + +.bi-table::before { + content: ""; } + +.bi-tablet-fill::before { + content: ""; } + +.bi-tablet-landscape-fill::before { + content: ""; } + +.bi-tablet-landscape::before { + content: ""; } + +.bi-tablet::before { + content: ""; } + +.bi-tag-fill::before { + content: ""; } + +.bi-tag::before { + content: ""; } + +.bi-tags-fill::before { + content: ""; } + +.bi-tags::before { + content: ""; } + +.bi-telegram::before { + content: ""; } + +.bi-telephone-fill::before { + content: ""; } + +.bi-telephone-forward-fill::before { + content: ""; } + +.bi-telephone-forward::before { + content: ""; } + +.bi-telephone-inbound-fill::before { + content: ""; } + +.bi-telephone-inbound::before { + content: ""; } + +.bi-telephone-minus-fill::before { + content: ""; } + +.bi-telephone-minus::before { + content: ""; } + +.bi-telephone-outbound-fill::before { + content: ""; } + +.bi-telephone-outbound::before { + content: ""; } + +.bi-telephone-plus-fill::before { + content: ""; } + +.bi-telephone-plus::before { + content: ""; } + +.bi-telephone-x-fill::before { + content: ""; } + +.bi-telephone-x::before { + content: ""; } + +.bi-telephone::before { + content: ""; } + +.bi-terminal-fill::before { + content: ""; } + +.bi-terminal::before { + content: ""; } + +.bi-text-center::before { + content: ""; } + +.bi-text-indent-left::before { + content: ""; } + +.bi-text-indent-right::before { + content: ""; } + +.bi-text-left::before { + content: ""; } + +.bi-text-paragraph::before { + content: ""; } + +.bi-text-right::before { + content: ""; } + +.bi-textarea-resize::before { + content: ""; } + +.bi-textarea-t::before { + content: ""; } + +.bi-textarea::before { + content: ""; } + +.bi-thermometer-half::before { + content: ""; } + +.bi-thermometer-high::before { + content: ""; } + +.bi-thermometer-low::before { + content: ""; } + +.bi-thermometer-snow::before { + content: ""; } + +.bi-thermometer-sun::before { + content: ""; } + +.bi-thermometer::before { + content: ""; } + +.bi-three-dots-vertical::before { + content: ""; } + +.bi-three-dots::before { + content: ""; } + +.bi-toggle-off::before { + content: ""; } + +.bi-toggle-on::before { + content: ""; } + +.bi-toggle2-off::before { + content: ""; } + +.bi-toggle2-on::before { + content: ""; } + +.bi-toggles::before { + content: ""; } + +.bi-toggles2::before { + content: ""; } + +.bi-tools::before { + content: ""; } + +.bi-tornado::before { + content: ""; } + +.bi-trash-fill::before { + content: ""; } + +.bi-trash::before { + content: ""; } + +.bi-trash2-fill::before { + content: ""; } + +.bi-trash2::before { + content: ""; } + +.bi-tree-fill::before { + content: ""; } + +.bi-tree::before { + content: ""; } + +.bi-triangle-fill::before { + content: ""; } + +.bi-triangle-half::before { + content: ""; } + +.bi-triangle::before { + content: ""; } + +.bi-trophy-fill::before { + content: ""; } + +.bi-trophy::before { + content: ""; } + +.bi-tropical-storm::before { + content: ""; } + +.bi-truck-flatbed::before { + content: ""; } + +.bi-truck::before { + content: ""; } + +.bi-tsunami::before { + content: ""; } + +.bi-tv-fill::before { + content: ""; } + +.bi-tv::before { + content: ""; } + +.bi-twitch::before { + content: ""; } + +.bi-twitter::before { + content: ""; } + +.bi-type-bold::before { + content: ""; } + +.bi-type-h1::before { + content: ""; } + +.bi-type-h2::before { + content: ""; } + +.bi-type-h3::before { + content: ""; } + +.bi-type-italic::before { + content: ""; } + +.bi-type-strikethrough::before { + content: ""; } + +.bi-type-underline::before { + content: ""; } + +.bi-type::before { + content: ""; } + +.bi-ui-checks-grid::before { + content: ""; } + +.bi-ui-checks::before { + content: ""; } + +.bi-ui-radios-grid::before { + content: ""; } + +.bi-ui-radios::before { + content: ""; } + +.bi-umbrella-fill::before { + content: ""; } + +.bi-umbrella::before { + content: ""; } + +.bi-union::before { + content: ""; } + +.bi-unlock-fill::before { + content: ""; } + +.bi-unlock::before { + content: ""; } + +.bi-upc-scan::before { + content: ""; } + +.bi-upc::before { + content: ""; } + +.bi-upload::before { + content: ""; } + +.bi-vector-pen::before { + content: ""; } + +.bi-view-list::before { + content: ""; } + +.bi-view-stacked::before { + content: ""; } + +.bi-vinyl-fill::before { + content: ""; } + +.bi-vinyl::before { + content: ""; } + +.bi-voicemail::before { + content: ""; } + +.bi-volume-down-fill::before { + content: ""; } + +.bi-volume-down::before { + content: ""; } + +.bi-volume-mute-fill::before { + content: ""; } + +.bi-volume-mute::before { + content: ""; } + +.bi-volume-off-fill::before { + content: ""; } + +.bi-volume-off::before { + content: ""; } + +.bi-volume-up-fill::before { + content: ""; } + +.bi-volume-up::before { + content: ""; } + +.bi-vr::before { + content: ""; } + +.bi-wallet-fill::before { + content: ""; } + +.bi-wallet::before { + content: ""; } + +.bi-wallet2::before { + content: ""; } + +.bi-watch::before { + content: ""; } + +.bi-water::before { + content: ""; } + +.bi-whatsapp::before { + content: ""; } + +.bi-wifi-1::before { + content: ""; } + +.bi-wifi-2::before { + content: ""; } + +.bi-wifi-off::before { + content: ""; } + +.bi-wifi::before { + content: ""; } + +.bi-wind::before { + content: ""; } + +.bi-window-dock::before { + content: ""; } + +.bi-window-sidebar::before { + content: ""; } + +.bi-window::before { + content: ""; } + +.bi-wrench::before { + content: ""; } + +.bi-x-circle-fill::before { + content: ""; } + +.bi-x-circle::before { + content: ""; } + +.bi-x-diamond-fill::before { + content: ""; } + +.bi-x-diamond::before { + content: ""; } + +.bi-x-octagon-fill::before { + content: ""; } + +.bi-x-octagon::before { + content: ""; } + +.bi-x-square-fill::before { + content: ""; } + +.bi-x-square::before { + content: ""; } + +.bi-x::before { + content: ""; } + +.bi-youtube::before { + content: ""; } + +.bi-zoom-in::before { + content: ""; } + +.bi-zoom-out::before { + content: ""; } + +.bi-bank::before { + content: ""; } + +.bi-bank2::before { + content: ""; } + +.bi-bell-slash-fill::before { + content: ""; } + +.bi-bell-slash::before { + content: ""; } + +.bi-cash-coin::before { + content: ""; } + +.bi-check-lg::before { + content: ""; } + +.bi-coin::before { + content: ""; } + +.bi-currency-bitcoin::before { + content: ""; } + +.bi-currency-dollar::before { + content: ""; } + +.bi-currency-euro::before { + content: ""; } + +.bi-currency-exchange::before { + content: ""; } + +.bi-currency-pound::before { + content: ""; } + +.bi-currency-yen::before { + content: ""; } + +.bi-dash-lg::before { + content: ""; } + +.bi-exclamation-lg::before { + content: ""; } + +.bi-file-earmark-pdf-fill::before { + content: ""; } + +.bi-file-earmark-pdf::before { + content: ""; } + +.bi-file-pdf-fill::before { + content: ""; } + +.bi-file-pdf::before { + content: ""; } + +.bi-gender-ambiguous::before { + content: ""; } + +.bi-gender-female::before { + content: ""; } + +.bi-gender-male::before { + content: ""; } + +.bi-gender-trans::before { + content: ""; } + +.bi-headset-vr::before { + content: ""; } + +.bi-info-lg::before { + content: ""; } + +.bi-mastodon::before { + content: ""; } + +.bi-messenger::before { + content: ""; } + +.bi-piggy-bank-fill::before { + content: ""; } + +.bi-piggy-bank::before { + content: ""; } + +.bi-pin-map-fill::before { + content: ""; } + +.bi-pin-map::before { + content: ""; } + +.bi-plus-lg::before { + content: ""; } + +.bi-question-lg::before { + content: ""; } + +.bi-recycle::before { + content: ""; } + +.bi-reddit::before { + content: ""; } + +.bi-safe-fill::before { + content: ""; } + +.bi-safe2-fill::before { + content: ""; } + +.bi-safe2::before { + content: ""; } + +.bi-sd-card-fill::before { + content: ""; } + +.bi-sd-card::before { + content: ""; } + +.bi-skype::before { + content: ""; } + +.bi-slash-lg::before { + content: ""; } + +.bi-translate::before { + content: ""; } + +.bi-x-lg::before { + content: ""; } + +.bi-safe::before { + content: ""; } + +.bi-apple::before { + content: ""; } + +.bi-microsoft::before { + content: ""; } + +.bi-windows::before { + content: ""; } + +.bi-behance::before { + content: ""; } + +.bi-dribbble::before { + content: ""; } + +.bi-line::before { + content: ""; } + +.bi-medium::before { + content: ""; } + +.bi-paypal::before { + content: ""; } + +.bi-pinterest::before { + content: ""; } + +.bi-signal::before { + content: ""; } + +.bi-snapchat::before { + content: ""; } + +.bi-spotify::before { + content: ""; } + +.bi-stack-overflow::before { + content: ""; } + +.bi-strava::before { + content: ""; } + +.bi-wordpress::before { + content: ""; } + +.bi-vimeo::before { + content: ""; } + +.bi-activity::before { + content: ""; } + +.bi-easel2-fill::before { + content: ""; } + +.bi-easel2::before { + content: ""; } + +.bi-easel3-fill::before { + content: ""; } + +.bi-easel3::before { + content: ""; } + +.bi-fan::before { + content: ""; } + +.bi-fingerprint::before { + content: ""; } + +.bi-graph-down-arrow::before { + content: ""; } + +.bi-graph-up-arrow::before { + content: ""; } + +.bi-hypnotize::before { + content: ""; } + +.bi-magic::before { + content: ""; } + +.bi-person-rolodex::before { + content: ""; } + +.bi-person-video::before { + content: ""; } + +.bi-person-video2::before { + content: ""; } + +.bi-person-video3::before { + content: ""; } + +.bi-person-workspace::before { + content: ""; } + +.bi-radioactive::before { + content: ""; } + +.bi-webcam-fill::before { + content: ""; } + +.bi-webcam::before { + content: ""; } + +.bi-yin-yang::before { + content: ""; } + +.bi-bandaid-fill::before { + content: ""; } + +.bi-bandaid::before { + content: ""; } + +.bi-bluetooth::before { + content: ""; } + +.bi-body-text::before { + content: ""; } + +.bi-boombox::before { + content: ""; } + +.bi-boxes::before { + content: ""; } + +.bi-dpad-fill::before { + content: ""; } + +.bi-dpad::before { + content: ""; } + +.bi-ear-fill::before { + content: ""; } + +.bi-ear::before { + content: ""; } + +.bi-envelope-check-fill::before { + content: ""; } + +.bi-envelope-check::before { + content: ""; } + +.bi-envelope-dash-fill::before { + content: ""; } + +.bi-envelope-dash::before { + content: ""; } + +.bi-envelope-exclamation-fill::before { + content: ""; } + +.bi-envelope-exclamation::before { + content: ""; } + +.bi-envelope-plus-fill::before { + content: ""; } + +.bi-envelope-plus::before { + content: ""; } + +.bi-envelope-slash-fill::before { + content: ""; } + +.bi-envelope-slash::before { + content: ""; } + +.bi-envelope-x-fill::before { + content: ""; } + +.bi-envelope-x::before { + content: ""; } + +.bi-explicit-fill::before { + content: ""; } + +.bi-explicit::before { + content: ""; } + +.bi-git::before { + content: ""; } + +.bi-infinity::before { + content: ""; } + +.bi-list-columns-reverse::before { + content: ""; } + +.bi-list-columns::before { + content: ""; } + +.bi-meta::before { + content: ""; } + +.bi-nintendo-switch::before { + content: ""; } + +.bi-pc-display-horizontal::before { + content: ""; } + +.bi-pc-display::before { + content: ""; } + +.bi-pc-horizontal::before { + content: ""; } + +.bi-pc::before { + content: ""; } + +.bi-playstation::before { + content: ""; } + +.bi-plus-slash-minus::before { + content: ""; } + +.bi-projector-fill::before { + content: ""; } + +.bi-projector::before { + content: ""; } + +.bi-qr-code-scan::before { + content: ""; } + +.bi-qr-code::before { + content: ""; } + +.bi-quora::before { + content: ""; } + +.bi-quote::before { + content: ""; } + +.bi-robot::before { + content: ""; } + +.bi-send-check-fill::before { + content: ""; } + +.bi-send-check::before { + content: ""; } + +.bi-send-dash-fill::before { + content: ""; } + +.bi-send-dash::before { + content: ""; } + +.bi-send-exclamation-fill::before { + content: ""; } + +.bi-send-exclamation::before { + content: ""; } + +.bi-send-fill::before { + content: ""; } + +.bi-send-plus-fill::before { + content: ""; } + +.bi-send-plus::before { + content: ""; } + +.bi-send-slash-fill::before { + content: ""; } + +.bi-send-slash::before { + content: ""; } + +.bi-send-x-fill::before { + content: ""; } + +.bi-send-x::before { + content: ""; } + +.bi-send::before { + content: ""; } + +.bi-steam::before { + content: ""; } + +.bi-terminal-dash::before { + content: ""; } + +.bi-terminal-plus::before { + content: ""; } + +.bi-terminal-split::before { + content: ""; } + +.bi-ticket-detailed-fill::before { + content: ""; } + +.bi-ticket-detailed::before { + content: ""; } + +.bi-ticket-fill::before { + content: ""; } + +.bi-ticket-perforated-fill::before { + content: ""; } + +.bi-ticket-perforated::before { + content: ""; } + +.bi-ticket::before { + content: ""; } + +.bi-tiktok::before { + content: ""; } + +.bi-window-dash::before { + content: ""; } + +.bi-window-desktop::before { + content: ""; } + +.bi-window-fullscreen::before { + content: ""; } + +.bi-window-plus::before { + content: ""; } + +.bi-window-split::before { + content: ""; } + +.bi-window-stack::before { + content: ""; } + +.bi-window-x::before { + content: ""; } + +.bi-xbox::before { + content: ""; } + +.bi-ethernet::before { + content: ""; } + +.bi-hdmi-fill::before { + content: ""; } + +.bi-hdmi::before { + content: ""; } + +.bi-usb-c-fill::before { + content: ""; } + +.bi-usb-c::before { + content: ""; } + +.bi-usb-fill::before { + content: ""; } + +.bi-usb-plug-fill::before { + content: ""; } + +.bi-usb-plug::before { + content: ""; } + +.bi-usb-symbol::before { + content: ""; } + +.bi-usb::before { + content: ""; } + +.bi-boombox-fill::before { + content: ""; } + +.bi-displayport::before { + content: ""; } + +.bi-gpu-card::before { + content: ""; } + +.bi-memory::before { + content: ""; } + +.bi-modem-fill::before { + content: ""; } + +.bi-modem::before { + content: ""; } + +.bi-motherboard-fill::before { + content: ""; } + +.bi-motherboard::before { + content: ""; } + +.bi-optical-audio-fill::before { + content: ""; } + +.bi-optical-audio::before { + content: ""; } + +.bi-pci-card::before { + content: ""; } + +.bi-router-fill::before { + content: ""; } + +.bi-router::before { + content: ""; } + +.bi-thunderbolt-fill::before { + content: ""; } + +.bi-thunderbolt::before { + content: ""; } + +.bi-usb-drive-fill::before { + content: ""; } + +.bi-usb-drive::before { + content: ""; } + +.bi-usb-micro-fill::before { + content: ""; } + +.bi-usb-micro::before { + content: ""; } + +.bi-usb-mini-fill::before { + content: ""; } + +.bi-usb-mini::before { + content: ""; } + +.bi-cloud-haze2::before { + content: ""; } + +.bi-device-hdd-fill::before { + content: ""; } + +.bi-device-hdd::before { + content: ""; } + +.bi-device-ssd-fill::before { + content: ""; } + +.bi-device-ssd::before { + content: ""; } + +.bi-displayport-fill::before { + content: ""; } + +.bi-mortarboard-fill::before { + content: ""; } + +.bi-mortarboard::before { + content: ""; } + +.bi-terminal-x::before { + content: ""; } + +.bi-arrow-through-heart-fill::before { + content: ""; } + +.bi-arrow-through-heart::before { + content: ""; } + +.bi-badge-sd-fill::before { + content: ""; } + +.bi-badge-sd::before { + content: ""; } + +.bi-bag-heart-fill::before { + content: ""; } + +.bi-bag-heart::before { + content: ""; } + +.bi-balloon-fill::before { + content: ""; } + +.bi-balloon-heart-fill::before { + content: ""; } + +.bi-balloon-heart::before { + content: ""; } + +.bi-balloon::before { + content: ""; } + +.bi-box2-fill::before { + content: ""; } + +.bi-box2-heart-fill::before { + content: ""; } + +.bi-box2-heart::before { + content: ""; } + +.bi-box2::before { + content: ""; } + +.bi-braces-asterisk::before { + content: ""; } + +.bi-calendar-heart-fill::before { + content: ""; } + +.bi-calendar-heart::before { + content: ""; } + +.bi-calendar2-heart-fill::before { + content: ""; } + +.bi-calendar2-heart::before { + content: ""; } + +.bi-chat-heart-fill::before { + content: ""; } + +.bi-chat-heart::before { + content: ""; } + +.bi-chat-left-heart-fill::before { + content: ""; } + +.bi-chat-left-heart::before { + content: ""; } + +.bi-chat-right-heart-fill::before { + content: ""; } + +.bi-chat-right-heart::before { + content: ""; } + +.bi-chat-square-heart-fill::before { + content: ""; } + +.bi-chat-square-heart::before { + content: ""; } + +.bi-clipboard-check-fill::before { + content: ""; } + +.bi-clipboard-data-fill::before { + content: ""; } + +.bi-clipboard-fill::before { + content: ""; } + +.bi-clipboard-heart-fill::before { + content: ""; } + +.bi-clipboard-heart::before { + content: ""; } + +.bi-clipboard-minus-fill::before { + content: ""; } + +.bi-clipboard-plus-fill::before { + content: ""; } + +.bi-clipboard-pulse::before { + content: ""; } + +.bi-clipboard-x-fill::before { + content: ""; } + +.bi-clipboard2-check-fill::before { + content: ""; } + +.bi-clipboard2-check::before { + content: ""; } + +.bi-clipboard2-data-fill::before { + content: ""; } + +.bi-clipboard2-data::before { + content: ""; } + +.bi-clipboard2-fill::before { + content: ""; } + +.bi-clipboard2-heart-fill::before { + content: ""; } + +.bi-clipboard2-heart::before { + content: ""; } + +.bi-clipboard2-minus-fill::before { + content: ""; } + +.bi-clipboard2-minus::before { + content: ""; } + +.bi-clipboard2-plus-fill::before { + content: ""; } + +.bi-clipboard2-plus::before { + content: ""; } + +.bi-clipboard2-pulse-fill::before { + content: ""; } + +.bi-clipboard2-pulse::before { + content: ""; } + +.bi-clipboard2-x-fill::before { + content: ""; } + +.bi-clipboard2-x::before { + content: ""; } + +.bi-clipboard2::before { + content: ""; } + +.bi-emoji-kiss-fill::before { + content: ""; } + +.bi-emoji-kiss::before { + content: ""; } + +.bi-envelope-heart-fill::before { + content: ""; } + +.bi-envelope-heart::before { + content: ""; } + +.bi-envelope-open-heart-fill::before { + content: ""; } + +.bi-envelope-open-heart::before { + content: ""; } + +.bi-envelope-paper-fill::before { + content: ""; } + +.bi-envelope-paper-heart-fill::before { + content: ""; } + +.bi-envelope-paper-heart::before { + content: ""; } + +.bi-envelope-paper::before { + content: ""; } + +.bi-filetype-aac::before { + content: ""; } + +.bi-filetype-ai::before { + content: ""; } + +.bi-filetype-bmp::before { + content: ""; } + +.bi-filetype-cs::before { + content: ""; } + +.bi-filetype-css::before { + content: ""; } + +.bi-filetype-csv::before { + content: ""; } + +.bi-filetype-doc::before { + content: ""; } + +.bi-filetype-docx::before { + content: ""; } + +.bi-filetype-exe::before { + content: ""; } + +.bi-filetype-gif::before { + content: ""; } + +.bi-filetype-heic::before { + content: ""; } + +.bi-filetype-html::before { + content: ""; } + +.bi-filetype-java::before { + content: ""; } + +.bi-filetype-jpg::before { + content: ""; } + +.bi-filetype-js::before { + content: ""; } + +.bi-filetype-jsx::before { + content: ""; } + +.bi-filetype-key::before { + content: ""; } + +.bi-filetype-m4p::before { + content: ""; } + +.bi-filetype-md::before { + content: ""; } + +.bi-filetype-mdx::before { + content: ""; } + +.bi-filetype-mov::before { + content: ""; } + +.bi-filetype-mp3::before { + content: ""; } + +.bi-filetype-mp4::before { + content: ""; } + +.bi-filetype-otf::before { + content: ""; } + +.bi-filetype-pdf::before { + content: ""; } + +.bi-filetype-php::before { + content: ""; } + +.bi-filetype-png::before { + content: ""; } + +.bi-filetype-ppt::before { + content: ""; } + +.bi-filetype-psd::before { + content: ""; } + +.bi-filetype-py::before { + content: ""; } + +.bi-filetype-raw::before { + content: ""; } + +.bi-filetype-rb::before { + content: ""; } + +.bi-filetype-sass::before { + content: ""; } + +.bi-filetype-scss::before { + content: ""; } + +.bi-filetype-sh::before { + content: ""; } + +.bi-filetype-svg::before { + content: ""; } + +.bi-filetype-tiff::before { + content: ""; } + +.bi-filetype-tsx::before { + content: ""; } + +.bi-filetype-ttf::before { + content: ""; } + +.bi-filetype-txt::before { + content: ""; } + +.bi-filetype-wav::before { + content: ""; } + +.bi-filetype-woff::before { + content: ""; } + +.bi-filetype-xls::before { + content: ""; } + +.bi-filetype-xml::before { + content: ""; } + +.bi-filetype-yml::before { + content: ""; } + +.bi-heart-arrow::before { + content: ""; } + +.bi-heart-pulse-fill::before { + content: ""; } + +.bi-heart-pulse::before { + content: ""; } + +.bi-heartbreak-fill::before { + content: ""; } + +.bi-heartbreak::before { + content: ""; } + +.bi-hearts::before { + content: ""; } + +.bi-hospital-fill::before { + content: ""; } + +.bi-hospital::before { + content: ""; } + +.bi-house-heart-fill::before { + content: ""; } + +.bi-house-heart::before { + content: ""; } + +.bi-incognito::before { + content: ""; } + +.bi-magnet-fill::before { + content: ""; } + +.bi-magnet::before { + content: ""; } + +.bi-person-heart::before { + content: ""; } + +.bi-person-hearts::before { + content: ""; } + +.bi-phone-flip::before { + content: ""; } + +.bi-plugin::before { + content: ""; } + +.bi-postage-fill::before { + content: ""; } + +.bi-postage-heart-fill::before { + content: ""; } + +.bi-postage-heart::before { + content: ""; } + +.bi-postage::before { + content: ""; } + +.bi-postcard-fill::before { + content: ""; } + +.bi-postcard-heart-fill::before { + content: ""; } + +.bi-postcard-heart::before { + content: ""; } + +.bi-postcard::before { + content: ""; } + +.bi-search-heart-fill::before { + content: ""; } + +.bi-search-heart::before { + content: ""; } + +.bi-sliders2-vertical::before { + content: ""; } + +.bi-sliders2::before { + content: ""; } + +.bi-trash3-fill::before { + content: ""; } + +.bi-trash3::before { + content: ""; } + +.bi-valentine::before { + content: ""; } + +.bi-valentine2::before { + content: ""; } + +.bi-wrench-adjustable-circle-fill::before { + content: ""; } + +.bi-wrench-adjustable-circle::before { + content: ""; } + +.bi-wrench-adjustable::before { + content: ""; } + +.bi-filetype-json::before { + content: ""; } + +.bi-filetype-pptx::before { + content: ""; } + +.bi-filetype-xlsx::before { + content: ""; } + +.bi-1-circle-fill::before { + content: ""; } + +.bi-1-circle::before { + content: ""; } + +.bi-1-square-fill::before { + content: ""; } + +.bi-1-square::before { + content: ""; } + +.bi-2-circle-fill::before { + content: ""; } + +.bi-2-circle::before { + content: ""; } + +.bi-2-square-fill::before { + content: ""; } + +.bi-2-square::before { + content: ""; } + +.bi-3-circle-fill::before { + content: ""; } + +.bi-3-circle::before { + content: ""; } + +.bi-3-square-fill::before { + content: ""; } + +.bi-3-square::before { + content: ""; } + +.bi-4-circle-fill::before { + content: ""; } + +.bi-4-circle::before { + content: ""; } + +.bi-4-square-fill::before { + content: ""; } + +.bi-4-square::before { + content: ""; } + +.bi-5-circle-fill::before { + content: ""; } + +.bi-5-circle::before { + content: ""; } + +.bi-5-square-fill::before { + content: ""; } + +.bi-5-square::before { + content: ""; } + +.bi-6-circle-fill::before { + content: ""; } + +.bi-6-circle::before { + content: ""; } + +.bi-6-square-fill::before { + content: ""; } + +.bi-6-square::before { + content: ""; } + +.bi-7-circle-fill::before { + content: ""; } + +.bi-7-circle::before { + content: ""; } + +.bi-7-square-fill::before { + content: ""; } + +.bi-7-square::before { + content: ""; } + +.bi-8-circle-fill::before { + content: ""; } + +.bi-8-circle::before { + content: ""; } + +.bi-8-square-fill::before { + content: ""; } + +.bi-8-square::before { + content: ""; } + +.bi-9-circle-fill::before { + content: ""; } + +.bi-9-circle::before { + content: ""; } + +.bi-9-square-fill::before { + content: ""; } + +.bi-9-square::before { + content: ""; } + +.bi-airplane-engines-fill::before { + content: ""; } + +.bi-airplane-engines::before { + content: ""; } + +.bi-airplane-fill::before { + content: ""; } + +.bi-airplane::before { + content: ""; } + +.bi-alexa::before { + content: ""; } + +.bi-alipay::before { + content: ""; } + +.bi-android::before { + content: ""; } + +.bi-android2::before { + content: ""; } + +.bi-box-fill::before { + content: ""; } + +.bi-box-seam-fill::before { + content: ""; } + +.bi-browser-chrome::before { + content: ""; } + +.bi-browser-edge::before { + content: ""; } + +.bi-browser-firefox::before { + content: ""; } + +.bi-browser-safari::before { + content: ""; } + +.bi-c-circle-fill::before { + content: ""; } + +.bi-c-circle::before { + content: ""; } + +.bi-c-square-fill::before { + content: ""; } + +.bi-c-square::before { + content: ""; } + +.bi-capsule-pill::before { + content: ""; } + +.bi-capsule::before { + content: ""; } + +.bi-car-front-fill::before { + content: ""; } + +.bi-car-front::before { + content: ""; } + +.bi-cassette-fill::before { + content: ""; } + +.bi-cassette::before { + content: ""; } + +.bi-cc-circle-fill::before { + content: ""; } + +.bi-cc-circle::before { + content: ""; } + +.bi-cc-square-fill::before { + content: ""; } + +.bi-cc-square::before { + content: ""; } + +.bi-cup-hot-fill::before { + content: ""; } + +.bi-cup-hot::before { + content: ""; } + +.bi-currency-rupee::before { + content: ""; } + +.bi-dropbox::before { + content: ""; } + +.bi-escape::before { + content: ""; } + +.bi-fast-forward-btn-fill::before { + content: ""; } + +.bi-fast-forward-btn::before { + content: ""; } + +.bi-fast-forward-circle-fill::before { + content: ""; } + +.bi-fast-forward-circle::before { + content: ""; } + +.bi-fast-forward-fill::before { + content: ""; } + +.bi-fast-forward::before { + content: ""; } + +.bi-filetype-sql::before { + content: ""; } + +.bi-fire::before { + content: ""; } + +.bi-google-play::before { + content: ""; } + +.bi-h-circle-fill::before { + content: ""; } + +.bi-h-circle::before { + content: ""; } + +.bi-h-square-fill::before { + content: ""; } + +.bi-h-square::before { + content: ""; } + +.bi-indent::before { + content: ""; } + +.bi-lungs-fill::before { + content: ""; } + +.bi-lungs::before { + content: ""; } + +.bi-microsoft-teams::before { + content: ""; } + +.bi-p-circle-fill::before { + content: ""; } + +.bi-p-circle::before { + content: ""; } + +.bi-p-square-fill::before { + content: ""; } + +.bi-p-square::before { + content: ""; } + +.bi-pass-fill::before { + content: ""; } + +.bi-pass::before { + content: ""; } + +.bi-prescription::before { + content: ""; } + +.bi-prescription2::before { + content: ""; } + +.bi-r-circle-fill::before { + content: ""; } + +.bi-r-circle::before { + content: ""; } + +.bi-r-square-fill::before { + content: ""; } + +.bi-r-square::before { + content: ""; } + +.bi-repeat-1::before { + content: ""; } + +.bi-repeat::before { + content: ""; } + +.bi-rewind-btn-fill::before { + content: ""; } + +.bi-rewind-btn::before { + content: ""; } + +.bi-rewind-circle-fill::before { + content: ""; } + +.bi-rewind-circle::before { + content: ""; } + +.bi-rewind-fill::before { + content: ""; } + +.bi-rewind::before { + content: ""; } + +.bi-train-freight-front-fill::before { + content: ""; } + +.bi-train-freight-front::before { + content: ""; } + +.bi-train-front-fill::before { + content: ""; } + +.bi-train-front::before { + content: ""; } + +.bi-train-lightrail-front-fill::before { + content: ""; } + +.bi-train-lightrail-front::before { + content: ""; } + +.bi-truck-front-fill::before { + content: ""; } + +.bi-truck-front::before { + content: ""; } + +.bi-ubuntu::before { + content: ""; } + +.bi-unindent::before { + content: ""; } + +.bi-unity::before { + content: ""; } + +.bi-universal-access-circle::before { + content: ""; } + +.bi-universal-access::before { + content: ""; } + +.bi-virus::before { + content: ""; } + +.bi-virus2::before { + content: ""; } + +.bi-wechat::before { + content: ""; } + +.bi-yelp::before { + content: ""; } + +.bi-sign-stop-fill::before { + content: ""; } + +.bi-sign-stop-lights-fill::before { + content: ""; } + +.bi-sign-stop-lights::before { + content: ""; } + +.bi-sign-stop::before { + content: ""; } + +.bi-sign-turn-left-fill::before { + content: ""; } + +.bi-sign-turn-left::before { + content: ""; } + +.bi-sign-turn-right-fill::before { + content: ""; } + +.bi-sign-turn-right::before { + content: ""; } + +.bi-sign-turn-slight-left-fill::before { + content: ""; } + +.bi-sign-turn-slight-left::before { + content: ""; } + +.bi-sign-turn-slight-right-fill::before { + content: ""; } + +.bi-sign-turn-slight-right::before { + content: ""; } + +.bi-sign-yield-fill::before { + content: ""; } + +.bi-sign-yield::before { + content: ""; } + +.bi-ev-station-fill::before { + content: ""; } + +.bi-ev-station::before { + content: ""; } + +.bi-fuel-pump-diesel-fill::before { + content: ""; } + +.bi-fuel-pump-diesel::before { + content: ""; } + +.bi-fuel-pump-fill::before { + content: ""; } + +.bi-fuel-pump::before { + content: ""; } + +.bi-0-circle-fill::before { + content: ""; } + +.bi-0-circle::before { + content: ""; } + +.bi-0-square-fill::before { + content: ""; } + +.bi-0-square::before { + content: ""; } + +.bi-rocket-fill::before { + content: ""; } + +.bi-rocket-takeoff-fill::before { + content: ""; } + +.bi-rocket-takeoff::before { + content: ""; } + +.bi-rocket::before { + content: ""; } + +.bi-stripe::before { + content: ""; } + +.bi-subscript::before { + content: ""; } + +.bi-superscript::before { + content: ""; } + +.bi-trello::before { + content: ""; } + +.bi-envelope-at-fill::before { + content: ""; } + +.bi-envelope-at::before { + content: ""; } + +.bi-regex::before { + content: ""; } + +.bi-text-wrap::before { + content: ""; } + +.bi-sign-dead-end-fill::before { + content: ""; } + +.bi-sign-dead-end::before { + content: ""; } + +.bi-sign-do-not-enter-fill::before { + content: ""; } + +.bi-sign-do-not-enter::before { + content: ""; } + +.bi-sign-intersection-fill::before { + content: ""; } + +.bi-sign-intersection-side-fill::before { + content: ""; } + +.bi-sign-intersection-side::before { + content: ""; } + +.bi-sign-intersection-t-fill::before { + content: ""; } + +.bi-sign-intersection-t::before { + content: ""; } + +.bi-sign-intersection-y-fill::before { + content: ""; } + +.bi-sign-intersection-y::before { + content: ""; } + +.bi-sign-intersection::before { + content: ""; } + +.bi-sign-merge-left-fill::before { + content: ""; } + +.bi-sign-merge-left::before { + content: ""; } + +.bi-sign-merge-right-fill::before { + content: ""; } + +.bi-sign-merge-right::before { + content: ""; } + +.bi-sign-no-left-turn-fill::before { + content: ""; } + +.bi-sign-no-left-turn::before { + content: ""; } + +.bi-sign-no-parking-fill::before { + content: ""; } + +.bi-sign-no-parking::before { + content: ""; } + +.bi-sign-no-right-turn-fill::before { + content: ""; } + +.bi-sign-no-right-turn::before { + content: ""; } + +.bi-sign-railroad-fill::before { + content: ""; } + +.bi-sign-railroad::before { + content: ""; } + +.bi-building-add::before { + content: ""; } + +.bi-building-check::before { + content: ""; } + +.bi-building-dash::before { + content: ""; } + +.bi-building-down::before { + content: ""; } + +.bi-building-exclamation::before { + content: ""; } + +.bi-building-fill-add::before { + content: ""; } + +.bi-building-fill-check::before { + content: ""; } + +.bi-building-fill-dash::before { + content: ""; } + +.bi-building-fill-down::before { + content: ""; } + +.bi-building-fill-exclamation::before { + content: ""; } + +.bi-building-fill-gear::before { + content: ""; } + +.bi-building-fill-lock::before { + content: ""; } + +.bi-building-fill-slash::before { + content: ""; } + +.bi-building-fill-up::before { + content: ""; } + +.bi-building-fill-x::before { + content: ""; } + +.bi-building-fill::before { + content: ""; } + +.bi-building-gear::before { + content: ""; } + +.bi-building-lock::before { + content: ""; } + +.bi-building-slash::before { + content: ""; } + +.bi-building-up::before { + content: ""; } + +.bi-building-x::before { + content: ""; } + +.bi-buildings-fill::before { + content: ""; } + +.bi-buildings::before { + content: ""; } + +.bi-bus-front-fill::before { + content: ""; } + +.bi-bus-front::before { + content: ""; } + +.bi-ev-front-fill::before { + content: ""; } + +.bi-ev-front::before { + content: ""; } + +.bi-globe-americas::before { + content: ""; } + +.bi-globe-asia-australia::before { + content: ""; } + +.bi-globe-central-south-asia::before { + content: ""; } + +.bi-globe-europe-africa::before { + content: ""; } + +.bi-house-add-fill::before { + content: ""; } + +.bi-house-add::before { + content: ""; } + +.bi-house-check-fill::before { + content: ""; } + +.bi-house-check::before { + content: ""; } + +.bi-house-dash-fill::before { + content: ""; } + +.bi-house-dash::before { + content: ""; } + +.bi-house-down-fill::before { + content: ""; } + +.bi-house-down::before { + content: ""; } + +.bi-house-exclamation-fill::before { + content: ""; } + +.bi-house-exclamation::before { + content: ""; } + +.bi-house-gear-fill::before { + content: ""; } + +.bi-house-gear::before { + content: ""; } + +.bi-house-lock-fill::before { + content: ""; } + +.bi-house-lock::before { + content: ""; } + +.bi-house-slash-fill::before { + content: ""; } + +.bi-house-slash::before { + content: ""; } + +.bi-house-up-fill::before { + content: ""; } + +.bi-house-up::before { + content: ""; } + +.bi-house-x-fill::before { + content: ""; } + +.bi-house-x::before { + content: ""; } + +.bi-person-add::before { + content: ""; } + +.bi-person-down::before { + content: ""; } + +.bi-person-exclamation::before { + content: ""; } + +.bi-person-fill-add::before { + content: ""; } + +.bi-person-fill-check::before { + content: ""; } + +.bi-person-fill-dash::before { + content: ""; } + +.bi-person-fill-down::before { + content: ""; } + +.bi-person-fill-exclamation::before { + content: ""; } + +.bi-person-fill-gear::before { + content: ""; } + +.bi-person-fill-lock::before { + content: ""; } + +.bi-person-fill-slash::before { + content: ""; } + +.bi-person-fill-up::before { + content: ""; } + +.bi-person-fill-x::before { + content: ""; } + +.bi-person-gear::before { + content: ""; } + +.bi-person-lock::before { + content: ""; } + +.bi-person-slash::before { + content: ""; } + +.bi-person-up::before { + content: ""; } + +.bi-scooter::before { + content: ""; } + +.bi-taxi-front-fill::before { + content: ""; } + +.bi-taxi-front::before { + content: ""; } + +.bi-amd::before { + content: ""; } + +.bi-database-add::before { + content: ""; } + +.bi-database-check::before { + content: ""; } + +.bi-database-dash::before { + content: ""; } + +.bi-database-down::before { + content: ""; } + +.bi-database-exclamation::before { + content: ""; } + +.bi-database-fill-add::before { + content: ""; } + +.bi-database-fill-check::before { + content: ""; } + +.bi-database-fill-dash::before { + content: ""; } + +.bi-database-fill-down::before { + content: ""; } + +.bi-database-fill-exclamation::before { + content: ""; } + +.bi-database-fill-gear::before { + content: ""; } + +.bi-database-fill-lock::before { + content: ""; } + +.bi-database-fill-slash::before { + content: ""; } + +.bi-database-fill-up::before { + content: ""; } + +.bi-database-fill-x::before { + content: ""; } + +.bi-database-fill::before { + content: ""; } + +.bi-database-gear::before { + content: ""; } + +.bi-database-lock::before { + content: ""; } + +.bi-database-slash::before { + content: ""; } + +.bi-database-up::before { + content: ""; } + +.bi-database-x::before { + content: ""; } + +.bi-database::before { + content: ""; } + +.bi-houses-fill::before { + content: ""; } + +.bi-houses::before { + content: ""; } + +.bi-nvidia::before { + content: ""; } + +.bi-person-vcard-fill::before { + content: ""; } + +.bi-person-vcard::before { + content: ""; } + +.bi-sina-weibo::before { + content: ""; } + +.bi-tencent-qq::before { + content: ""; } + +.bi-wikipedia::before { + content: ""; } diff --git a/gnuviechadmin/static/css/bootstrap-theme.min.css b/gnuviechadmin/static/css/bootstrap-theme.min.css deleted file mode 100644 index ac8dd55..0000000 --- a/gnuviechadmin/static/css/bootstrap-theme.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Bootstrap v3.3.2 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */.btn-danger,.btn-default,.btn-info,.btn-primary,.btn-success,.btn-warning{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-danger.active,.btn-danger:active,.btn-default.active,.btn-default:active,.btn-info.active,.btn-info:active,.btn-primary.active,.btn-primary:active,.btn-success.active,.btn-success:active,.btn-warning.active,.btn-warning:active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-danger .badge,.btn-default .badge,.btn-info .badge,.btn-primary .badge,.btn-success .badge,.btn-warning .badge{text-shadow:none}.btn.active,.btn:active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:focus,.btn-default:hover{background-color:#e0e0e0;background-position:0 -15px}.btn-default.active,.btn-default:active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default:disabled,.btn-default[disabled]{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-o-linear-gradient(top,#337ab7 0,#265a88 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#265a88));background-image:linear-gradient(to bottom,#337ab7 0,#265a88 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#245580}.btn-primary:focus,.btn-primary:hover{background-color:#265a88;background-position:0 -15px}.btn-primary.active,.btn-primary:active{background-color:#265a88;border-color:#245580}.btn-primary.disabled,.btn-primary:disabled,.btn-primary[disabled]{background-color:#265a88;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:focus,.btn-success:hover{background-color:#419641;background-position:0 -15px}.btn-success.active,.btn-success:active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success:disabled,.btn-success[disabled]{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:focus,.btn-info:hover{background-color:#2aabd2;background-position:0 -15px}.btn-info.active,.btn-info:active{background-color:#2aabd2;border-color:#28a4c9}.btn-info.disabled,.btn-info:disabled,.btn-info[disabled]{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:focus,.btn-warning:hover{background-color:#eb9316;background-position:0 -15px}.btn-warning.active,.btn-warning:active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning:disabled,.btn-warning[disabled]{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:focus,.btn-danger:hover{background-color:#c12e2a;background-position:0 -15px}.btn-danger.active,.btn-danger:active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger:disabled,.btn-danger[disabled]{background-color:#c12e2a;background-image:none}.img-thumbnail,.thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{background-color:#2e6da4;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-o-linear-gradient(top,#dbdbdb 0,#e2e2e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dbdbdb),to(#e2e2e2));background-image:linear-gradient(to bottom,#dbdbdb 0,#e2e2e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.open>a{background-image:-webkit-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-o-linear-gradient(top,#080808 0,#0f0f0f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#080808),to(#0f0f0f));background-image:linear-gradient(to bottom,#080808 0,#0f0f0f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-fixed-bottom,.navbar-fixed-top,.navbar-static-top{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-o-linear-gradient(top,#337ab7 0,#286090 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#286090));background-image:linear-gradient(to bottom,#337ab7 0,#286090 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{text-shadow:0 -1px 0 #286090;background-image:-webkit-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2b669a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2b669a));background-image:linear-gradient(to bottom,#337ab7 0,#2b669a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);background-repeat:repeat-x;border-color:#2b669a}.list-group-item.active .badge,.list-group-item.active:focus .badge,.list-group-item.active:hover .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-o-linear-gradient(top,#337ab7 0,#2e6da4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#337ab7),to(#2e6da4));background-image:linear-gradient(to bottom,#337ab7 0,#2e6da4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} \ No newline at end of file diff --git a/gnuviechadmin/static/css/bootstrap.min.css b/gnuviechadmin/static/css/bootstrap.min.css index 28f154d..f06f523 100644 --- a/gnuviechadmin/static/css/bootstrap.min.css +++ b/gnuviechadmin/static/css/bootstrap.min.css @@ -1,5 +1,7 @@ -/*! - * Bootstrap v3.3.2 (http://getbootstrap.com) - * Copyright 2011-2015 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - *//*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff!important}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\2a"}.glyphicon-plus:before{content:"\2b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date],input[type=time],input[type=datetime-local],input[type=month]{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px \9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.form-group-sm .form-control{height:30px;line-height:30px}select[multiple].form-group-sm .form-control,textarea.form-group-sm .form-control{height:auto}.form-group-sm .form-control-static{height:30px;padding:5px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.form-group-lg .form-control{height:46px;line-height:46px}select[multiple].form-group-lg .form-control,textarea.form-group-lg .form-control{height:auto}.form-group-lg .form-control-static{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.33px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{pointer-events:none;cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.active,.btn-default.focus,.btn-default:active,.btn-default:focus,.btn-default:hover,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default.disabled.active,.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled],.btn-default[disabled].active,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default,fieldset[disabled] .btn-default.active,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.active,.btn-primary.focus,.btn-primary:active,.btn-primary:focus,.btn-primary:hover,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary.disabled.active,.btn-primary.disabled.focus,.btn-primary.disabled:active,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled],.btn-primary[disabled].active,.btn-primary[disabled].focus,.btn-primary[disabled]:active,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-primary.active,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:active,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.active,.btn-success.focus,.btn-success:active,.btn-success:focus,.btn-success:hover,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success.disabled.active,.btn-success.disabled.focus,.btn-success.disabled:active,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled],.btn-success[disabled].active,.btn-success[disabled].focus,.btn-success[disabled]:active,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success,fieldset[disabled] .btn-success.active,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:active,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.active,.btn-info.focus,.btn-info:active,.btn-info:focus,.btn-info:hover,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info.disabled.active,.btn-info.disabled.focus,.btn-info.disabled:active,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled],.btn-info[disabled].active,.btn-info[disabled].focus,.btn-info[disabled]:active,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info,fieldset[disabled] .btn-info.active,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:active,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.active,.btn-warning.focus,.btn-warning:active,.btn-warning:focus,.btn-warning:hover,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning.disabled.active,.btn-warning.disabled.focus,.btn-warning.disabled:active,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled],.btn-warning[disabled].active,.btn-warning[disabled].focus,.btn-warning[disabled]:active,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-warning.active,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:active,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.active,.btn-danger.focus,.btn-danger:active,.btn-danger:focus,.btn-danger:hover,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger.disabled.active,.btn-danger.disabled.focus,.btn-danger.disabled:active,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled],.btn-danger[disabled].active,.btn-danger[disabled].focus,.btn-danger[disabled]:active,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger,fieldset[disabled] .btn-danger.active,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:active,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none;visibility:hidden}.collapse.in{display:block;visibility:visible}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px solid;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px solid}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none;visibility:hidden}.tab-content>.active{display:block;visibility:visible}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important;visibility:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:2;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding:30px 15px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding:48px 0}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item{color:#555}a.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:absolute;top:0;right:0;left:0;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{min-height:16.43px;padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-weight:400;line-height:1.4;visibility:visible;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;text-decoration:none;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-weight:400;line-height:1.42857143;text-align:left;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000;perspective:1000}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;margin-top:-10px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000 \9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-15px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-15px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-15px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important;visibility:hidden!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} \ No newline at end of file +@charset "UTF-8";/*! + * Bootstrap v5.2.3 (https://getbootstrap.com/) + * Copyright 2011-2022 The Bootstrap Authors + * Copyright 2011-2022 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-body-color-rgb:33,37,41;--bs-body-bg-rgb:255,255,255;--bs-font-sans-serif:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-bg:#fff;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.375rem;--bs-border-radius-sm:0.25rem;--bs-border-radius-lg:0.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-2xl:2rem;--bs-border-radius-pill:50rem;--bs-link-color:#0d6efd;--bs-link-hover-color:#0a58ca;--bs-code-color:#d63384;--bs-highlight-bg:#fff3cd}*,::after,::before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:1rem 0;color:inherit;border:0;border-top:1px solid;opacity:.25}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem;font-weight:500;line-height:1.2}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media (min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media (min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media (min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media (min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.1875em;background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:var(--bs-link-color);text-decoration:underline}a:hover{color:var(--bs-link-hover-color)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:var(--bs-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.1875rem .375rem;font-size:.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.25rem}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:#6c757d;text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;font-size:calc(1.275rem + .3vw);line-height:inherit}@media (min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:textfield}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:calc(1.625rem + 4.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-1{font-size:5rem}}.display-2{font-size:calc(1.575rem + 3.9vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-size:calc(1.525rem + 3.3vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-3{font-size:4rem}}.display-4{font-size:calc(1.475rem + 2.7vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-size:calc(1.425rem + 2.1vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-5{font-size:3rem}}.display-6{font-size:calc(1.375rem + 1.5vw);font-weight:300;line-height:1.2}@media (min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-1rem;margin-bottom:1rem;font-size:.875em;color:#6c757d}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid var(--bs-border-color);border-radius:.375rem;max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:.875em;color:#6c757d}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-right:auto;margin-left:auto}@media (min-width:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media (min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media (min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0 0%}.row-cols-auto>*{flex:0 0 auto;width:auto}.row-cols-1>*{flex:0 0 auto;width:100%}.row-cols-2>*{flex:0 0 auto;width:50%}.row-cols-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-4>*{flex:0 0 auto;width:25%}.row-cols-5>*{flex:0 0 auto;width:20%}.row-cols-6>*{flex:0 0 auto;width:16.6666666667%}.col-auto{flex:0 0 auto;width:auto}.col-1{flex:0 0 auto;width:8.33333333%}.col-2{flex:0 0 auto;width:16.66666667%}.col-3{flex:0 0 auto;width:25%}.col-4{flex:0 0 auto;width:33.33333333%}.col-5{flex:0 0 auto;width:41.66666667%}.col-6{flex:0 0 auto;width:50%}.col-7{flex:0 0 auto;width:58.33333333%}.col-8{flex:0 0 auto;width:66.66666667%}.col-9{flex:0 0 auto;width:75%}.col-10{flex:0 0 auto;width:83.33333333%}.col-11{flex:0 0 auto;width:91.66666667%}.col-12{flex:0 0 auto;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.25rem}.g-1,.gy-1{--bs-gutter-y:0.25rem}.g-2,.gx-2{--bs-gutter-x:0.5rem}.g-2,.gy-2{--bs-gutter-y:0.5rem}.g-3,.gx-3{--bs-gutter-x:1rem}.g-3,.gy-3{--bs-gutter-y:1rem}.g-4,.gx-4{--bs-gutter-x:1.5rem}.g-4,.gy-4{--bs-gutter-y:1.5rem}.g-5,.gx-5{--bs-gutter-x:3rem}.g-5,.gy-5{--bs-gutter-y:3rem}@media (min-width:576px){.col-sm{flex:1 0 0%}.row-cols-sm-auto>*{flex:0 0 auto;width:auto}.row-cols-sm-1>*{flex:0 0 auto;width:100%}.row-cols-sm-2>*{flex:0 0 auto;width:50%}.row-cols-sm-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-sm-4>*{flex:0 0 auto;width:25%}.row-cols-sm-5>*{flex:0 0 auto;width:20%}.row-cols-sm-6>*{flex:0 0 auto;width:16.6666666667%}.col-sm-auto{flex:0 0 auto;width:auto}.col-sm-1{flex:0 0 auto;width:8.33333333%}.col-sm-2{flex:0 0 auto;width:16.66666667%}.col-sm-3{flex:0 0 auto;width:25%}.col-sm-4{flex:0 0 auto;width:33.33333333%}.col-sm-5{flex:0 0 auto;width:41.66666667%}.col-sm-6{flex:0 0 auto;width:50%}.col-sm-7{flex:0 0 auto;width:58.33333333%}.col-sm-8{flex:0 0 auto;width:66.66666667%}.col-sm-9{flex:0 0 auto;width:75%}.col-sm-10{flex:0 0 auto;width:83.33333333%}.col-sm-11{flex:0 0 auto;width:91.66666667%}.col-sm-12{flex:0 0 auto;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.25rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.25rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:0.5rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:0.5rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:1rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:1rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:1.5rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:1.5rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:3rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:3rem}}@media (min-width:768px){.col-md{flex:1 0 0%}.row-cols-md-auto>*{flex:0 0 auto;width:auto}.row-cols-md-1>*{flex:0 0 auto;width:100%}.row-cols-md-2>*{flex:0 0 auto;width:50%}.row-cols-md-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-md-4>*{flex:0 0 auto;width:25%}.row-cols-md-5>*{flex:0 0 auto;width:20%}.row-cols-md-6>*{flex:0 0 auto;width:16.6666666667%}.col-md-auto{flex:0 0 auto;width:auto}.col-md-1{flex:0 0 auto;width:8.33333333%}.col-md-2{flex:0 0 auto;width:16.66666667%}.col-md-3{flex:0 0 auto;width:25%}.col-md-4{flex:0 0 auto;width:33.33333333%}.col-md-5{flex:0 0 auto;width:41.66666667%}.col-md-6{flex:0 0 auto;width:50%}.col-md-7{flex:0 0 auto;width:58.33333333%}.col-md-8{flex:0 0 auto;width:66.66666667%}.col-md-9{flex:0 0 auto;width:75%}.col-md-10{flex:0 0 auto;width:83.33333333%}.col-md-11{flex:0 0 auto;width:91.66666667%}.col-md-12{flex:0 0 auto;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.25rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.25rem}.g-md-2,.gx-md-2{--bs-gutter-x:0.5rem}.g-md-2,.gy-md-2{--bs-gutter-y:0.5rem}.g-md-3,.gx-md-3{--bs-gutter-x:1rem}.g-md-3,.gy-md-3{--bs-gutter-y:1rem}.g-md-4,.gx-md-4{--bs-gutter-x:1.5rem}.g-md-4,.gy-md-4{--bs-gutter-y:1.5rem}.g-md-5,.gx-md-5{--bs-gutter-x:3rem}.g-md-5,.gy-md-5{--bs-gutter-y:3rem}}@media (min-width:992px){.col-lg{flex:1 0 0%}.row-cols-lg-auto>*{flex:0 0 auto;width:auto}.row-cols-lg-1>*{flex:0 0 auto;width:100%}.row-cols-lg-2>*{flex:0 0 auto;width:50%}.row-cols-lg-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-lg-4>*{flex:0 0 auto;width:25%}.row-cols-lg-5>*{flex:0 0 auto;width:20%}.row-cols-lg-6>*{flex:0 0 auto;width:16.6666666667%}.col-lg-auto{flex:0 0 auto;width:auto}.col-lg-1{flex:0 0 auto;width:8.33333333%}.col-lg-2{flex:0 0 auto;width:16.66666667%}.col-lg-3{flex:0 0 auto;width:25%}.col-lg-4{flex:0 0 auto;width:33.33333333%}.col-lg-5{flex:0 0 auto;width:41.66666667%}.col-lg-6{flex:0 0 auto;width:50%}.col-lg-7{flex:0 0 auto;width:58.33333333%}.col-lg-8{flex:0 0 auto;width:66.66666667%}.col-lg-9{flex:0 0 auto;width:75%}.col-lg-10{flex:0 0 auto;width:83.33333333%}.col-lg-11{flex:0 0 auto;width:91.66666667%}.col-lg-12{flex:0 0 auto;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.25rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.25rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:0.5rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:0.5rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:1rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:1rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:1.5rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:1.5rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:3rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:3rem}}@media (min-width:1200px){.col-xl{flex:1 0 0%}.row-cols-xl-auto>*{flex:0 0 auto;width:auto}.row-cols-xl-1>*{flex:0 0 auto;width:100%}.row-cols-xl-2>*{flex:0 0 auto;width:50%}.row-cols-xl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xl-4>*{flex:0 0 auto;width:25%}.row-cols-xl-5>*{flex:0 0 auto;width:20%}.row-cols-xl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xl-auto{flex:0 0 auto;width:auto}.col-xl-1{flex:0 0 auto;width:8.33333333%}.col-xl-2{flex:0 0 auto;width:16.66666667%}.col-xl-3{flex:0 0 auto;width:25%}.col-xl-4{flex:0 0 auto;width:33.33333333%}.col-xl-5{flex:0 0 auto;width:41.66666667%}.col-xl-6{flex:0 0 auto;width:50%}.col-xl-7{flex:0 0 auto;width:58.33333333%}.col-xl-8{flex:0 0 auto;width:66.66666667%}.col-xl-9{flex:0 0 auto;width:75%}.col-xl-10{flex:0 0 auto;width:83.33333333%}.col-xl-11{flex:0 0 auto;width:91.66666667%}.col-xl-12{flex:0 0 auto;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.25rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.25rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:0.5rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:0.5rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:1rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:1rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:1.5rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:1.5rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:3rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:3rem}}@media (min-width:1400px){.col-xxl{flex:1 0 0%}.row-cols-xxl-auto>*{flex:0 0 auto;width:auto}.row-cols-xxl-1>*{flex:0 0 auto;width:100%}.row-cols-xxl-2>*{flex:0 0 auto;width:50%}.row-cols-xxl-3>*{flex:0 0 auto;width:33.3333333333%}.row-cols-xxl-4>*{flex:0 0 auto;width:25%}.row-cols-xxl-5>*{flex:0 0 auto;width:20%}.row-cols-xxl-6>*{flex:0 0 auto;width:16.6666666667%}.col-xxl-auto{flex:0 0 auto;width:auto}.col-xxl-1{flex:0 0 auto;width:8.33333333%}.col-xxl-2{flex:0 0 auto;width:16.66666667%}.col-xxl-3{flex:0 0 auto;width:25%}.col-xxl-4{flex:0 0 auto;width:33.33333333%}.col-xxl-5{flex:0 0 auto;width:41.66666667%}.col-xxl-6{flex:0 0 auto;width:50%}.col-xxl-7{flex:0 0 auto;width:58.33333333%}.col-xxl-8{flex:0 0 auto;width:66.66666667%}.col-xxl-9{flex:0 0 auto;width:75%}.col-xxl-10{flex:0 0 auto;width:83.33333333%}.col-xxl-11{flex:0 0 auto;width:91.66666667%}.col-xxl-12{flex:0 0 auto;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.25rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.25rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:0.5rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:0.5rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:1rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:1rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:1.5rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:1.5rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:3rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:3rem}}.table{--bs-table-color:var(--bs-body-color);--bs-table-bg:transparent;--bs-table-border-color:var(--bs-border-color);--bs-table-accent-bg:transparent;--bs-table-striped-color:var(--bs-body-color);--bs-table-striped-bg:rgba(0, 0, 0, 0.05);--bs-table-active-color:var(--bs-body-color);--bs-table-active-bg:rgba(0, 0, 0, 0.1);--bs-table-hover-color:var(--bs-body-color);--bs-table-hover-bg:rgba(0, 0, 0, 0.075);width:100%;margin-bottom:1rem;color:var(--bs-table-color);vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*{padding:.5rem .5rem;background-color:var(--bs-table-bg);border-bottom-width:1px;box-shadow:inset 0 0 0 9999px var(--bs-table-accent-bg)}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table-group-divider{border-top:2px solid currentcolor}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem .25rem}.table-bordered>:not(caption)>*{border-width:1px 0}.table-bordered>:not(caption)>*>*{border-width:0 1px}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-striped-columns>:not(caption)>tr>:nth-child(2n){--bs-table-accent-bg:var(--bs-table-striped-bg);color:var(--bs-table-striped-color)}.table-active{--bs-table-accent-bg:var(--bs-table-active-bg);color:var(--bs-table-active-color)}.table-hover>tbody>tr:hover>*{--bs-table-accent-bg:var(--bs-table-hover-bg);color:var(--bs-table-hover-color)}.table-primary{--bs-table-color:#000;--bs-table-bg:#cfe2ff;--bs-table-border-color:#bacbe6;--bs-table-striped-bg:#c5d7f2;--bs-table-striped-color:#000;--bs-table-active-bg:#bacbe6;--bs-table-active-color:#000;--bs-table-hover-bg:#bfd1ec;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color:#000;--bs-table-bg:#e2e3e5;--bs-table-border-color:#cbccce;--bs-table-striped-bg:#d7d8da;--bs-table-striped-color:#000;--bs-table-active-bg:#cbccce;--bs-table-active-color:#000;--bs-table-hover-bg:#d1d2d4;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color:#000;--bs-table-bg:#d1e7dd;--bs-table-border-color:#bcd0c7;--bs-table-striped-bg:#c7dbd2;--bs-table-striped-color:#000;--bs-table-active-bg:#bcd0c7;--bs-table-active-color:#000;--bs-table-hover-bg:#c1d6cc;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color:#000;--bs-table-bg:#cff4fc;--bs-table-border-color:#badce3;--bs-table-striped-bg:#c5e8ef;--bs-table-striped-color:#000;--bs-table-active-bg:#badce3;--bs-table-active-color:#000;--bs-table-hover-bg:#bfe2e9;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color:#000;--bs-table-bg:#fff3cd;--bs-table-border-color:#e6dbb9;--bs-table-striped-bg:#f2e7c3;--bs-table-striped-color:#000;--bs-table-active-bg:#e6dbb9;--bs-table-active-color:#000;--bs-table-hover-bg:#ece1be;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color:#000;--bs-table-bg:#f8d7da;--bs-table-border-color:#dfc2c4;--bs-table-striped-bg:#eccccf;--bs-table-striped-color:#000;--bs-table-active-bg:#dfc2c4;--bs-table-active-color:#000;--bs-table-hover-bg:#e5c7ca;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color:#000;--bs-table-bg:#f8f9fa;--bs-table-border-color:#dfe0e1;--bs-table-striped-bg:#ecedee;--bs-table-striped-color:#000;--bs-table-active-bg:#dfe0e1;--bs-table-active-color:#000;--bs-table-hover-bg:#e5e6e7;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color:#fff;--bs-table-bg:#212529;--bs-table-border-color:#373b3e;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media (max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media (max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:#6c757d}.form-control{display:block;width:100%;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-radius:.375rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:#212529;background-color:#fff;border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-control::-webkit-date-and-time-value{height:1.5em}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control::placeholder{color:#6c757d;opacity:1}.form-control:disabled{background-color:#e9ecef;opacity:1}.form-control::-webkit-file-upload-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::file-selector-button{padding:.375rem .75rem;margin:-.375rem -.75rem;-webkit-margin-end:.75rem;margin-inline-end:.75rem;color:#212529;background-color:#e9ecef;pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:#dde0e3}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:#dde0e3}.form-control-plaintext{display:block;width:100%;padding:.375rem 0;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + 2px);padding:.25rem .5rem;font-size:.875rem;border-radius:.25rem}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + 2px);padding:.5rem 1rem;font-size:1.25rem;border-radius:.5rem}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + .75rem + 2px)}textarea.form-control-sm{min-height:calc(1.5em + .5rem + 2px)}textarea.form-control-lg{min-height:calc(1.5em + 1rem + 2px)}.form-control-color{width:3rem;height:calc(1.5em + .75rem + 2px);padding:.375rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0!important;border-radius:.375rem}.form-control-color::-webkit-color-swatch{border-radius:.375rem}.form-control-color.form-control-sm{height:calc(1.5em + .5rem + 2px)}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + 2px)}.form-select{display:block;width:100%;padding:.375rem 2.25rem .375rem .75rem;-moz-padding-start:calc(0.75rem - 3px);font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right .75rem center;background-size:16px 12px;border:1px solid #ced4da;border-radius:.375rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:.75rem;background-image:none}.form-select:disabled{background-color:#e9ecef}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 #212529}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:.25rem}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:.5rem}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-reverse{padding-right:1.5em;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0}.form-check-input{width:1em;height:1em;margin-top:.25em;vertical-align:top;background-color:#fff;background-repeat:no-repeat;background-position:center;background-size:contain;border:1px solid rgba(0,0,0,.25);-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-print-color-adjust:exact;color-adjust:exact;print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:#86b7fe;outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25)}.form-check-input:checked{background-color:#0d6efd;border-color:#0d6efd}.form-check-input:checked[type=checkbox]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#0d6efd;border-color:#0d6efd;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{cursor:default;opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{width:2em;margin-left:-2.5em;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e");background-position:left center;border-radius:2em;transition:background-position .15s ease-in-out}@media (prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:right center;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}.form-range{width:100%;height:1.5rem;padding:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .25rem rgba(13,110,253,.25)}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#0d6efd;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#b6d4fe}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#0d6efd;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#b6d4fe}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:#adb5bd}.form-range:disabled::-moz-range-thumb{background-color:#adb5bd}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + 2px);line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;width:100%;height:100%;padding:1rem .75rem;overflow:hidden;text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:1px solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media (prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem .75rem}.form-floating>.form-control-plaintext::-moz-placeholder,.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control-plaintext::placeholder,.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control-plaintext:not(:-moz-placeholder-shown),.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown),.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:-webkit-autofill,.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control-plaintext~label,.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{opacity:.65;transform:scale(.85) translateY(-.5rem) translateX(.15rem)}.form-floating>.form-control-plaintext~label{border-width:1px 0}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-floating,.input-group>.form-select{position:relative;flex:1 1 auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-floating:focus-within,.input-group>.form-select:focus{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.375rem .75rem;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.375rem}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:.5rem}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:.25rem}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:3rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select,.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select,.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:-1px;border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#198754}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(25,135,84,.9);border-radius:.375rem}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:#198754;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:#198754}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:#198754;box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-control-color.is-valid,.was-validated .form-control-color:valid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:#198754}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:#198754}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(25,135,84,.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#198754}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-valid,.input-group>.form-floating:not(:focus-within).is-valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-control:not(:focus):valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.was-validated .input-group>.form-select:not(:focus):valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.375rem}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:#dc3545;padding-right:calc(1.5em + .75rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .1875rem) center;background-size:calc(.75em + .375rem) calc(.75em + .375rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + .75rem);background-position:top calc(.375em + .1875rem) right calc(.375em + .1875rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:#dc3545}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{padding-right:4.125rem;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"),url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e");background-position:right .75rem center,center right 2.25rem;background-size:16px 12px,calc(.75em + .375rem) calc(.75em + .375rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-control-color.is-invalid,.was-validated .form-control-color:invalid{width:calc(3rem + calc(1.5em + .75rem))}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:#dc3545}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:#dc3545}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(220,53,69,.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-invalid,.input-group>.form-floating:not(:focus-within).is-invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-control:not(:focus):invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.was-validated .input-group>.form-select:not(:focus):invalid{z-index:4}.btn{--bs-btn-padding-x:0.75rem;--bs-btn-padding-y:0.375rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight:400;--bs-btn-line-height:1.5;--bs-btn-color:#212529;--bs-btn-bg:transparent;--bs-btn-border-width:1px;--bs-btn-border-color:transparent;--bs-btn-border-radius:0.375rem;--bs-btn-hover-border-color:transparent;--bs-btn-box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.15),0 1px 1px rgba(0, 0, 0, 0.075);--bs-btn-disabled-opacity:0.65;--bs-btn-focus-box-shadow:0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y) var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width) solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,.btn.active,.btn.show,.btn:first-child:active,:not(.btn-check)+.btn:active{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}.btn-check:checked+.btn:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible,.btn:first-child:active:focus-visible,:not(.btn-check)+.btn:active:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-primary{--bs-btn-color:#fff;--bs-btn-bg:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0b5ed7;--bs-btn-hover-border-color:#0a58ca;--bs-btn-focus-shadow-rgb:49,132,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0a58ca;--bs-btn-active-border-color:#0a53be;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#0d6efd;--bs-btn-disabled-border-color:#0d6efd}.btn-secondary{--bs-btn-color:#fff;--bs-btn-bg:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#5c636a;--bs-btn-hover-border-color:#565e64;--bs-btn-focus-shadow-rgb:130,138,145;--bs-btn-active-color:#fff;--bs-btn-active-bg:#565e64;--bs-btn-active-border-color:#51585e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#6c757d;--bs-btn-disabled-border-color:#6c757d}.btn-success{--bs-btn-color:#fff;--bs-btn-bg:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#157347;--bs-btn-hover-border-color:#146c43;--bs-btn-focus-shadow-rgb:60,153,110;--bs-btn-active-color:#fff;--bs-btn-active-bg:#146c43;--bs-btn-active-border-color:#13653f;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#198754;--bs-btn-disabled-border-color:#198754}.btn-info{--bs-btn-color:#000;--bs-btn-bg:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#31d2f2;--bs-btn-hover-border-color:#25cff2;--bs-btn-focus-shadow-rgb:11,172,204;--bs-btn-active-color:#000;--bs-btn-active-bg:#3dd5f3;--bs-btn-active-border-color:#25cff2;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#0dcaf0;--bs-btn-disabled-border-color:#0dcaf0}.btn-warning{--bs-btn-color:#000;--bs-btn-bg:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffca2c;--bs-btn-hover-border-color:#ffc720;--bs-btn-focus-shadow-rgb:217,164,6;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffcd39;--bs-btn-active-border-color:#ffc720;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#ffc107;--bs-btn-disabled-border-color:#ffc107}.btn-danger{--bs-btn-color:#fff;--bs-btn-bg:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#bb2d3b;--bs-btn-hover-border-color:#b02a37;--bs-btn-focus-shadow-rgb:225,83,97;--bs-btn-active-color:#fff;--bs-btn-active-bg:#b02a37;--bs-btn-active-border-color:#a52834;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#dc3545;--bs-btn-disabled-border-color:#dc3545}.btn-light{--bs-btn-color:#000;--bs-btn-bg:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#d3d4d5;--bs-btn-hover-border-color:#c6c7c8;--bs-btn-focus-shadow-rgb:211,212,213;--bs-btn-active-color:#000;--bs-btn-active-bg:#c6c7c8;--bs-btn-active-border-color:#babbbc;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#f8f9fa;--bs-btn-disabled-border-color:#f8f9fa}.btn-dark{--bs-btn-color:#fff;--bs-btn-bg:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#424649;--bs-btn-hover-border-color:#373b3e;--bs-btn-focus-shadow-rgb:66,70,73;--bs-btn-active-color:#fff;--bs-btn-active-bg:#4d5154;--bs-btn-active-border-color:#373b3e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#212529;--bs-btn-disabled-border-color:#212529}.btn-outline-primary{--bs-btn-color:#0d6efd;--bs-btn-border-color:#0d6efd;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#0d6efd;--bs-btn-hover-border-color:#0d6efd;--bs-btn-focus-shadow-rgb:13,110,253;--bs-btn-active-color:#fff;--bs-btn-active-bg:#0d6efd;--bs-btn-active-border-color:#0d6efd;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0d6efd;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0d6efd;--bs-gradient:none}.btn-outline-secondary{--bs-btn-color:#6c757d;--bs-btn-border-color:#6c757d;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#6c757d;--bs-btn-hover-border-color:#6c757d;--bs-btn-focus-shadow-rgb:108,117,125;--bs-btn-active-color:#fff;--bs-btn-active-bg:#6c757d;--bs-btn-active-border-color:#6c757d;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#6c757d;--bs-gradient:none}.btn-outline-success{--bs-btn-color:#198754;--bs-btn-border-color:#198754;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#198754;--bs-btn-hover-border-color:#198754;--bs-btn-focus-shadow-rgb:25,135,84;--bs-btn-active-color:#fff;--bs-btn-active-bg:#198754;--bs-btn-active-border-color:#198754;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#198754;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#198754;--bs-gradient:none}.btn-outline-info{--bs-btn-color:#0dcaf0;--bs-btn-border-color:#0dcaf0;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#0dcaf0;--bs-btn-hover-border-color:#0dcaf0;--bs-btn-focus-shadow-rgb:13,202,240;--bs-btn-active-color:#000;--bs-btn-active-bg:#0dcaf0;--bs-btn-active-border-color:#0dcaf0;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#0dcaf0;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#0dcaf0;--bs-gradient:none}.btn-outline-warning{--bs-btn-color:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#ffc107;--bs-btn-hover-border-color:#ffc107;--bs-btn-focus-shadow-rgb:255,193,7;--bs-btn-active-color:#000;--bs-btn-active-bg:#ffc107;--bs-btn-active-border-color:#ffc107;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#ffc107;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#ffc107;--bs-gradient:none}.btn-outline-danger{--bs-btn-color:#dc3545;--bs-btn-border-color:#dc3545;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#dc3545;--bs-btn-hover-border-color:#dc3545;--bs-btn-focus-shadow-rgb:220,53,69;--bs-btn-active-color:#fff;--bs-btn-active-bg:#dc3545;--bs-btn-active-border-color:#dc3545;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#dc3545;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#dc3545;--bs-gradient:none}.btn-outline-light{--bs-btn-color:#f8f9fa;--bs-btn-border-color:#f8f9fa;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#f8f9fa;--bs-btn-hover-border-color:#f8f9fa;--bs-btn-focus-shadow-rgb:248,249,250;--bs-btn-active-color:#000;--bs-btn-active-bg:#f8f9fa;--bs-btn-active-border-color:#f8f9fa;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#f8f9fa;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#f8f9fa;--bs-gradient:none}.btn-outline-dark{--bs-btn-color:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#212529;--bs-btn-hover-border-color:#212529;--bs-btn-focus-shadow-rgb:33,37,41;--bs-btn-active-color:#fff;--bs-btn-active-bg:#212529;--bs-btn-active-border-color:#212529;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#212529;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#212529;--bs-gradient:none}.btn-link{--bs-btn-font-weight:400;--bs-btn-color:var(--bs-link-color);--bs-btn-bg:transparent;--bs-btn-border-color:transparent;--bs-btn-hover-color:var(--bs-link-hover-color);--bs-btn-hover-border-color:transparent;--bs-btn-active-color:var(--bs-link-hover-color);--bs-btn-active-border-color:transparent;--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-border-color:transparent;--bs-btn-box-shadow:none;--bs-btn-focus-shadow-rgb:49,132,253;text-decoration:underline}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-group-lg>.btn,.btn-lg{--bs-btn-padding-y:0.5rem;--bs-btn-padding-x:1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius:0.5rem}.btn-group-sm>.btn,.btn-sm{--bs-btn-padding-y:0.25rem;--bs-btn-padding-x:0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius:0.25rem}.fade{transition:opacity .15s linear}@media (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media (prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media (prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropdown-center,.dropend,.dropstart,.dropup,.dropup-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex:1000;--bs-dropdown-min-width:10rem;--bs-dropdown-padding-x:0;--bs-dropdown-padding-y:0.5rem;--bs-dropdown-spacer:0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color:#212529;--bs-dropdown-bg:#fff;--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-border-radius:0.375rem;--bs-dropdown-border-width:1px;--bs-dropdown-inner-border-radius:calc(0.375rem - 1px);--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-divider-margin-y:0.5rem;--bs-dropdown-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-dropdown-link-color:#212529;--bs-dropdown-link-hover-color:#1e2125;--bs-dropdown-link-hover-bg:#e9ecef;--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:#adb5bd;--bs-dropdown-item-padding-x:1rem;--bs-dropdown-item-padding-y:0.25rem;--bs-dropdown-header-color:#6c757d;--bs-dropdown-header-padding-x:1rem;--bs-dropdown-header-padding-y:0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y) var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width) solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media (min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media (min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media (min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--bs-dropdown-spacer)}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y) 0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;text-decoration:none;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y) var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color:#dee2e6;--bs-dropdown-bg:#343a40;--bs-dropdown-border-color:var(--bs-border-color-translucent);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color:#dee2e6;--bs-dropdown-link-hover-color:#fff;--bs-dropdown-divider-bg:var(--bs-border-color-translucent);--bs-dropdown-link-hover-bg:rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#0d6efd;--bs-dropdown-link-disabled-color:#adb5bd;--bs-dropdown-header-color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:1 1 auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:.375rem}.btn-group>.btn-group:not(:first-child),.btn-group>:not(.btn-check:first-child)+.btn{margin-left:-1px}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn.dropdown-toggle-split:first-child,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:-1px}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn~.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{--bs-nav-link-padding-x:1rem;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-link-color);--bs-nav-link-hover-color:var(--bs-link-hover-color);--bs-nav-link-disabled-color:#6c757d;display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--bs-nav-link-padding-y) var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media (prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:var(--bs-nav-link-hover-color)}.nav-link.disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width:1px;--bs-nav-tabs-border-color:#dee2e6;--bs-nav-tabs-border-radius:0.375rem;--bs-nav-tabs-link-hover-border-color:#e9ecef #e9ecef #dee2e6;--bs-nav-tabs-link-active-color:#495057;--bs-nav-tabs-link-active-bg:#fff;--bs-nav-tabs-link-active-border-color:#dee2e6 #dee2e6 #fff;border-bottom:var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1 * var(--bs-nav-tabs-border-width));background:0 0;border:var(--bs-nav-tabs-border-width) solid transparent;border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius)}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-link.disabled,.nav-tabs .nav-link:disabled{color:var(--bs-nav-link-disabled-color);background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1 * var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--bs-nav-pills-border-radius:0.375rem;--bs-nav-pills-link-active-color:#fff;--bs-nav-pills-link-active-bg:#0d6efd}.nav-pills .nav-link{background:0 0;border:0;border-radius:var(--bs-nav-pills-border-radius)}.nav-pills .nav-link:disabled{color:var(--bs-nav-link-disabled-color);background-color:transparent;border-color:transparent}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg)}.nav-fill .nav-item,.nav-fill>.nav-link{flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-basis:0;flex-grow:1;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{--bs-navbar-padding-x:0;--bs-navbar-padding-y:0.5rem;--bs-navbar-color:rgba(0, 0, 0, 0.55);--bs-navbar-hover-color:rgba(0, 0, 0, 0.7);--bs-navbar-disabled-color:rgba(0, 0, 0, 0.3);--bs-navbar-active-color:rgba(0, 0, 0, 0.9);--bs-navbar-brand-padding-y:0.3125rem;--bs-navbar-brand-margin-end:1rem;--bs-navbar-brand-font-size:1.25rem;--bs-navbar-brand-color:rgba(0, 0, 0, 0.9);--bs-navbar-brand-hover-color:rgba(0, 0, 0, 0.9);--bs-navbar-nav-link-padding-x:0.5rem;--bs-navbar-toggler-padding-y:0.25rem;--bs-navbar-toggler-padding-x:0.75rem;--bs-navbar-toggler-font-size:1.25rem;--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color:rgba(0, 0, 0, 0.1);--bs-navbar-toggler-border-radius:0.375rem;--bs-navbar-toggler-focus-width:0.25rem;--bs-navbar-toggler-transition:box-shadow 0.15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y) var(--bs-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x:0;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-navbar-color);--bs-nav-link-hover-color:var(--bs-navbar-hover-color);--bs-nav-link-disabled-color:var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .show>.nav-link{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:focus,.navbar-text a:hover{color:var(--bs-navbar-active-color)}.navbar-collapse{flex-basis:100%;flex-grow:1;align-items:center}.navbar-toggler{padding:var(--bs-navbar-toggler-padding-y) var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:transparent;border:var(--bs-border-width) solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition)}@media (prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:center;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media (min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media (min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:transparent!important;border:0!important;transform:none!important;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark{--bs-navbar-color:rgba(255, 255, 255, 0.55);--bs-navbar-hover-color:rgba(255, 255, 255, 0.75);--bs-navbar-disabled-color:rgba(255, 255, 255, 0.25);--bs-navbar-active-color:#fff;--bs-navbar-brand-color:#fff;--bs-navbar-brand-hover-color:#fff;--bs-navbar-toggler-border-color:rgba(255, 255, 255, 0.1);--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--bs-card-spacer-y:1rem;--bs-card-spacer-x:1rem;--bs-card-title-spacer-y:0.5rem;--bs-card-border-width:1px;--bs-card-border-color:var(--bs-border-color-translucent);--bs-card-border-radius:0.375rem;--bs-card-box-shadow: ;--bs-card-inner-border-radius:calc(0.375rem - 1px);--bs-card-cap-padding-y:0.5rem;--bs-card-cap-padding-x:1rem;--bs-card-cap-bg:rgba(0, 0, 0, 0.03);--bs-card-cap-color: ;--bs-card-height: ;--bs-card-color: ;--bs-card-bg:#fff;--bs-card-img-overlay-padding:1rem;--bs-card-group-margin:0.75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width) solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:1 1 auto;padding:var(--bs-card-spacer-y) var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y)}.card-subtitle{margin-top:calc(-.5 * var(--bs-card-title-spacer-y));margin-bottom:0}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius) 0 0}.card-footer{padding:var(--bs-card-cap-padding-y) var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width) solid var(--bs-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius) var(--bs-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-bottom:calc(-1 * var(--bs-card-cap-padding-y));margin-left:calc(-.5 * var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-left:calc(-.5 * var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--bs-card-group-margin)}@media (min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child) .card-header,.card-group>.card:not(:last-child) .card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child) .card-footer,.card-group>.card:not(:last-child) .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child) .card-header,.card-group>.card:not(:first-child) .card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child) .card-footer,.card-group>.card:not(:first-child) .card-img-bottom{border-bottom-left-radius:0}}.accordion{--bs-accordion-color:#212529;--bs-accordion-bg:#fff;--bs-accordion-transition:color 0.15s ease-in-out,background-color 0.15s ease-in-out,border-color 0.15s ease-in-out,box-shadow 0.15s ease-in-out,border-radius 0.15s ease;--bs-accordion-border-color:var(--bs-border-color);--bs-accordion-border-width:1px;--bs-accordion-border-radius:0.375rem;--bs-accordion-inner-border-radius:calc(0.375rem - 1px);--bs-accordion-btn-padding-x:1.25rem;--bs-accordion-btn-padding-y:1rem;--bs-accordion-btn-color:#212529;--bs-accordion-btn-bg:var(--bs-accordion-bg);--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-icon-width:1.25rem;--bs-accordion-btn-icon-transform:rotate(-180deg);--bs-accordion-btn-icon-transition:transform 0.2s ease-in-out;--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");--bs-accordion-btn-focus-border-color:#86b7fe;--bs-accordion-btn-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-accordion-body-padding-x:1.25rem;--bs-accordion-body-padding-y:1rem;--bs-accordion-active-color:#0c63e4;--bs-accordion-active-bg:#e7f1ff}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y) var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media (prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--bs-accordion-border-width)) 0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media (prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;border-color:var(--bs-accordion-btn-focus-border-color);outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width) solid var(--bs-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius)}.accordion-item:first-of-type .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-item:last-of-type .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:last-of-type .accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-body{padding:var(--bs-accordion-body-padding-y) var(--bs-accordion-body-padding-x)}.accordion-flush .accordion-collapse{border-width:0}.accordion-flush .accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush .accordion-item:first-child{border-top:0}.accordion-flush .accordion-item:last-child{border-bottom:0}.accordion-flush .accordion-item .accordion-button,.accordion-flush .accordion-item .accordion-button.collapsed{border-radius:0}.breadcrumb{--bs-breadcrumb-padding-x:0;--bs-breadcrumb-padding-y:0;--bs-breadcrumb-margin-bottom:1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color:#6c757d;--bs-breadcrumb-item-padding-x:0.5rem;--bs-breadcrumb-item-active-color:#6c757d;display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider, "/")}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x:0.75rem;--bs-pagination-padding-y:0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color:var(--bs-link-color);--bs-pagination-bg:#fff;--bs-pagination-border-width:1px;--bs-pagination-border-color:#dee2e6;--bs-pagination-border-radius:0.375rem;--bs-pagination-hover-color:var(--bs-link-hover-color);--bs-pagination-hover-bg:#e9ecef;--bs-pagination-hover-border-color:#dee2e6;--bs-pagination-focus-color:var(--bs-link-hover-color);--bs-pagination-focus-bg:#e9ecef;--bs-pagination-focus-box-shadow:0 0 0 0.25rem rgba(13, 110, 253, 0.25);--bs-pagination-active-color:#fff;--bs-pagination-active-bg:#0d6efd;--bs-pagination-active-border-color:#0d6efd;--bs-pagination-disabled-color:#6c757d;--bs-pagination-disabled-bg:#fff;--bs-pagination-disabled-border-color:#dee2e6;display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y) var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width) solid var(--bs-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media (prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.active>.page-link,.page-link.active{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color)}.disabled>.page-link,.page-link.disabled{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:-1px}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.pagination-lg{--bs-pagination-padding-x:1.5rem;--bs-pagination-padding-y:0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius:0.5rem}.pagination-sm{--bs-pagination-padding-x:0.5rem;--bs-pagination-padding-y:0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius:0.25rem}.badge{--bs-badge-padding-x:0.65em;--bs-badge-padding-y:0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight:700;--bs-badge-color:#fff;--bs-badge-border-radius:0.375rem;display:inline-block;padding:var(--bs-badge-padding-y) var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--bs-alert-bg:transparent;--bs-alert-padding-x:1rem;--bs-alert-padding-y:1rem;--bs-alert-margin-bottom:1rem;--bs-alert-color:inherit;--bs-alert-border-color:transparent;--bs-alert-border:1px solid var(--bs-alert-border-color);--bs-alert-border-radius:0.375rem;position:relative;padding:var(--bs-alert-padding-y) var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:3rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:1.25rem 1rem}.alert-primary{--bs-alert-color:#084298;--bs-alert-bg:#cfe2ff;--bs-alert-border-color:#b6d4fe}.alert-primary .alert-link{color:#06357a}.alert-secondary{--bs-alert-color:#41464b;--bs-alert-bg:#e2e3e5;--bs-alert-border-color:#d3d6d8}.alert-secondary .alert-link{color:#34383c}.alert-success{--bs-alert-color:#0f5132;--bs-alert-bg:#d1e7dd;--bs-alert-border-color:#badbcc}.alert-success .alert-link{color:#0c4128}.alert-info{--bs-alert-color:#055160;--bs-alert-bg:#cff4fc;--bs-alert-border-color:#b6effb}.alert-info .alert-link{color:#04414d}.alert-warning{--bs-alert-color:#664d03;--bs-alert-bg:#fff3cd;--bs-alert-border-color:#ffecb5}.alert-warning .alert-link{color:#523e02}.alert-danger{--bs-alert-color:#842029;--bs-alert-bg:#f8d7da;--bs-alert-border-color:#f5c2c7}.alert-danger .alert-link{color:#6a1a21}.alert-light{--bs-alert-color:#636464;--bs-alert-bg:#fefefe;--bs-alert-border-color:#fdfdfe}.alert-light .alert-link{color:#4f5050}.alert-dark{--bs-alert-color:#141619;--bs-alert-bg:#d3d3d4;--bs-alert-border-color:#bcbebf}.alert-dark .alert-link{color:#101214}@keyframes progress-bar-stripes{0%{background-position-x:1rem}}.progress{--bs-progress-height:1rem;--bs-progress-font-size:0.75rem;--bs-progress-bg:#e9ecef;--bs-progress-border-radius:0.375rem;--bs-progress-box-shadow:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-progress-bar-color:#fff;--bs-progress-bar-bg:#0d6efd;--bs-progress-bar-transition:width 0.6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@media (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:var(--bs-progress-height) var(--bs-progress-height)}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media (prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color:#212529;--bs-list-group-bg:#fff;--bs-list-group-border-color:rgba(0, 0, 0, 0.125);--bs-list-group-border-width:1px;--bs-list-group-border-radius:0.375rem;--bs-list-group-item-padding-x:1rem;--bs-list-group-item-padding-y:0.5rem;--bs-list-group-action-color:#495057;--bs-list-group-action-hover-color:#495057;--bs-list-group-action-hover-bg:#f8f9fa;--bs-list-group-action-active-color:#212529;--bs-list-group-action-active-bg:#e9ecef;--bs-list-group-disabled-color:#6c757d;--bs-list-group-disabled-bg:#fff;--bs-list-group-active-color:#fff;--bs-list-group-active-bg:#0d6efd;--bs-list-group-active-border-color:#0d6efd;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section, ".") ". ";counter-increment:section}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y) var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width) solid var(--bs-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media (min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media (min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{color:#084298;background-color:#cfe2ff}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#084298;background-color:#bacbe6}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#084298;border-color:#084298}.list-group-item-secondary{color:#41464b;background-color:#e2e3e5}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#41464b;background-color:#cbccce}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#41464b;border-color:#41464b}.list-group-item-success{color:#0f5132;background-color:#d1e7dd}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#0f5132;background-color:#bcd0c7}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#0f5132;border-color:#0f5132}.list-group-item-info{color:#055160;background-color:#cff4fc}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#055160;background-color:#badce3}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#055160;border-color:#055160}.list-group-item-warning{color:#664d03;background-color:#fff3cd}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#664d03;background-color:#e6dbb9}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#664d03;border-color:#664d03}.list-group-item-danger{color:#842029;background-color:#f8d7da}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#842029;background-color:#dfc2c4}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#842029;border-color:#842029}.list-group-item-light{color:#636464;background-color:#fefefe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#636464;background-color:#e5e5e5}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#636464;border-color:#636464}.list-group-item-dark{color:#141619;background-color:#d3d3d4}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#141619;background-color:#bebebf}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#141619;border-color:#141619}.btn-close{box-sizing:content-box;width:1em;height:1em;padding:.25em .25em;color:#000;background:transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat;border:0;border-radius:.375rem;opacity:.5}.btn-close:hover{color:#000;text-decoration:none;opacity:.75}.btn-close:focus{outline:0;box-shadow:0 0 0 .25rem rgba(13,110,253,.25);opacity:1}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:.25}.btn-close-white{filter:invert(1) grayscale(100%) brightness(200%)}.toast{--bs-toast-zindex:1090;--bs-toast-padding-x:0.75rem;--bs-toast-padding-y:0.5rem;--bs-toast-spacing:1.5rem;--bs-toast-max-width:350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg:rgba(255, 255, 255, 0.85);--bs-toast-border-width:1px;--bs-toast-border-color:var(--bs-border-color-translucent);--bs-toast-border-radius:0.375rem;--bs-toast-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-toast-header-color:#6c757d;--bs-toast-header-bg:rgba(255, 255, 255, 0.85);--bs-toast-header-border-color:rgba(0, 0, 0, 0.05);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width) solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex:1090;position:absolute;z-index:var(--bs-toast-zindex);width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--bs-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--bs-toast-padding-y) var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width) solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width))}.toast-header .btn-close{margin-right:calc(-.5 * var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex:1055;--bs-modal-width:500px;--bs-modal-padding:1rem;--bs-modal-margin:0.5rem;--bs-modal-color: ;--bs-modal-bg:#fff;--bs-modal-border-color:var(--bs-border-color-translucent);--bs-modal-border-width:1px;--bs-modal-border-radius:0.5rem;--bs-modal-box-shadow:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);--bs-modal-inner-border-radius:calc(0.5rem - 1px);--bs-modal-header-padding-x:1rem;--bs-modal-header-padding-y:1rem;--bs-modal-header-padding:1rem 1rem;--bs-modal-header-border-color:var(--bs-border-color);--bs-modal-header-border-width:1px;--bs-modal-title-line-height:1.5;--bs-modal-footer-gap:0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color:var(--bs-border-color);--bs-modal-footer-border-width:1px;position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transition:transform .3s ease-out;transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - var(--bs-modal-margin) * 2)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--bs-modal-margin) * 2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width) solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);outline:0}.modal-backdrop{--bs-backdrop-zindex:1050;--bs-backdrop-bg:#000;--bs-backdrop-opacity:0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;justify-content:space-between;padding:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width) solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y) * .5) calc(var(--bs-modal-header-padding-x) * .5);margin:calc(-.5 * var(--bs-modal-header-padding-y)) calc(-.5 * var(--bs-modal-header-padding-x)) calc(-.5 * var(--bs-modal-header-padding-y)) auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:1 1 auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * .5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width) solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap) * .5)}@media (min-width:576px){.modal{--bs-modal-margin:1.75rem;--bs-modal-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width:300px}}@media (min-width:992px){.modal-lg,.modal-xl{--bs-modal-width:800px}}@media (min-width:1200px){.modal-xl{--bs-modal-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-footer,.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@media (max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-footer,.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media (max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-footer,.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media (max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-footer,.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media (max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-footer,.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media (max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-footer,.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex:1080;--bs-tooltip-max-width:200px;--bs-tooltip-padding-x:0.5rem;--bs-tooltip-padding-y:0.25rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color:#fff;--bs-tooltip-bg:#000;--bs-tooltip-border-radius:0.375rem;--bs-tooltip-opacity:0.9;--bs-tooltip-arrow-width:0.8rem;--bs-tooltip-arrow-height:0.4rem;z-index:var(--bs-tooltip-zindex);display:block;padding:var(--bs-tooltip-arrow-height);margin:var(--bs-tooltip-margin);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:0}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:0;width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height) calc(var(--bs-tooltip-arrow-width) * .5) 0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:0}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:0;width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5) 0 calc(var(--bs-tooltip-arrow-width) * .5) var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y) var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius)}.popover{--bs-popover-zindex:1070;--bs-popover-max-width:276px;--bs-popover-font-size:0.875rem;--bs-popover-bg:#fff;--bs-popover-border-width:1px;--bs-popover-border-color:var(--bs-border-color-translucent);--bs-popover-border-radius:0.5rem;--bs-popover-inner-border-radius:calc(0.5rem - 1px);--bs-popover-box-shadow:0 0.5rem 1rem rgba(0, 0, 0, 0.15);--bs-popover-header-padding-x:1rem;--bs-popover-header-padding-y:0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color: ;--bs-popover-header-bg:#f0f0f0;--bs-popover-body-padding-x:1rem;--bs-popover-body-padding-y:1rem;--bs-popover-body-color:#212529;--bs-popover-arrow-width:1rem;--bs-popover-arrow-height:0.5rem;--bs-popover-arrow-border:var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-top>.popover-arrow::before{border-width:var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-end>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height) calc(var(--bs-popover-arrow-width) * .5) 0}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::before{border-width:0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:var(--bs-popover-arrow-width);margin-left:calc(-.5 * var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-header-bg)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-start>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5) 0 calc(var(--bs-popover-arrow-width) * .5) var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y) var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width) solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y) var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media (prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media (prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;border:0;opacity:.5;transition:opacity .15s ease}@media (prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0z'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%;list-style:none}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:0 1 auto;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:#fff;background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media (prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:#fff;text-align:center}.carousel-dark .carousel-control-next-icon,.carousel-dark .carousel-control-prev-icon{filter:invert(1) grayscale(100)}.carousel-dark .carousel-indicators [data-bs-target]{background-color:#000}.carousel-dark .carousel-caption{color:#000}.spinner-border,.spinner-grow{display:inline-block;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed) linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-border-width:0.25em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-border;border:var(--bs-spinner-border-width) solid currentcolor;border-right-color:transparent}.spinner-border-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem;--bs-spinner-border-width:0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem}@media (prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed:1.5s}}.offcanvas,.offcanvas-lg,.offcanvas-md,.offcanvas-sm,.offcanvas-xl,.offcanvas-xxl{--bs-offcanvas-zindex:1045;--bs-offcanvas-width:400px;--bs-offcanvas-height:30vh;--bs-offcanvas-padding-x:1rem;--bs-offcanvas-padding-y:1rem;--bs-offcanvas-color: ;--bs-offcanvas-bg:#fff;--bs-offcanvas-border-width:1px;--bs-offcanvas-border-color:var(--bs-border-color-translucent);--bs-offcanvas-box-shadow:0 0.125rem 0.25rem rgba(0, 0, 0, 0.075)}@media (max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}}@media (max-width:575.98px) and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none}}@media (max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}}@media (max-width:575.98px){.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}}@media (max-width:575.98px){.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}}@media (max-width:575.98px){.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}}@media (max-width:575.98px){.offcanvas-sm.show:not(.hiding),.offcanvas-sm.showing{transform:none}}@media (max-width:575.98px){.offcanvas-sm.hiding,.offcanvas-sm.show,.offcanvas-sm.showing{visibility:visible}}@media (min-width:576px){.offcanvas-sm{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}}@media (max-width:767.98px) and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none}}@media (max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}}@media (max-width:767.98px){.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}}@media (max-width:767.98px){.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}}@media (max-width:767.98px){.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}}@media (max-width:767.98px){.offcanvas-md.show:not(.hiding),.offcanvas-md.showing{transform:none}}@media (max-width:767.98px){.offcanvas-md.hiding,.offcanvas-md.show,.offcanvas-md.showing{visibility:visible}}@media (min-width:768px){.offcanvas-md{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}}@media (max-width:991.98px) and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none}}@media (max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}}@media (max-width:991.98px){.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}}@media (max-width:991.98px){.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}}@media (max-width:991.98px){.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}}@media (max-width:991.98px){.offcanvas-lg.show:not(.hiding),.offcanvas-lg.showing{transform:none}}@media (max-width:991.98px){.offcanvas-lg.hiding,.offcanvas-lg.show,.offcanvas-lg.showing{visibility:visible}}@media (min-width:992px){.offcanvas-lg{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}}@media (max-width:1199.98px) and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none}}@media (max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}}@media (max-width:1199.98px){.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}}@media (max-width:1199.98px){.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}}@media (max-width:1199.98px){.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}}@media (max-width:1199.98px){.offcanvas-xl.show:not(.hiding),.offcanvas-xl.showing{transform:none}}@media (max-width:1199.98px){.offcanvas-xl.hiding,.offcanvas-xl.show,.offcanvas-xl.showing{visibility:visible}}@media (min-width:1200px){.offcanvas-xl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}@media (max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}}@media (max-width:1399.98px) and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none}}@media (max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}}@media (max-width:1399.98px){.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}}@media (max-width:1399.98px){.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}}@media (max-width:1399.98px){.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}}@media (max-width:1399.98px){.offcanvas-xxl.show:not(.hiding),.offcanvas-xxl.showing{transform:none}}@media (max-width:1399.98px){.offcanvas-xxl.hiding,.offcanvas-xxl.show,.offcanvas-xxl.showing{visibility:visible}}@media (min-width:1400px){.offcanvas-xxl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:transparent!important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:transparent!important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:transform .3s ease-in-out}@media (prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.show:not(.hiding),.offcanvas.showing{transform:none}.offcanvas.hiding,.offcanvas.show,.offcanvas.showing{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;justify-content:space-between;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y) * .5) calc(var(--bs-offcanvas-padding-x) * .5);margin-top:calc(-.5 * var(--bs-offcanvas-padding-y));margin-right:calc(-.5 * var(--bs-offcanvas-padding-x));margin-bottom:calc(-.5 * var(--bs-offcanvas-padding-y))}.offcanvas-title{margin-bottom:0;line-height:1.5}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,0.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.text-bg-primary{color:#fff!important;background-color:RGBA(13,110,253,var(--bs-bg-opacity,1))!important}.text-bg-secondary{color:#fff!important;background-color:RGBA(108,117,125,var(--bs-bg-opacity,1))!important}.text-bg-success{color:#fff!important;background-color:RGBA(25,135,84,var(--bs-bg-opacity,1))!important}.text-bg-info{color:#000!important;background-color:RGBA(13,202,240,var(--bs-bg-opacity,1))!important}.text-bg-warning{color:#000!important;background-color:RGBA(255,193,7,var(--bs-bg-opacity,1))!important}.text-bg-danger{color:#fff!important;background-color:RGBA(220,53,69,var(--bs-bg-opacity,1))!important}.text-bg-light{color:#000!important;background-color:RGBA(248,249,250,var(--bs-bg-opacity,1))!important}.text-bg-dark{color:#fff!important;background-color:RGBA(33,37,41,var(--bs-bg-opacity,1))!important}.link-primary{color:#0d6efd!important}.link-primary:focus,.link-primary:hover{color:#0a58ca!important}.link-secondary{color:#6c757d!important}.link-secondary:focus,.link-secondary:hover{color:#565e64!important}.link-success{color:#198754!important}.link-success:focus,.link-success:hover{color:#146c43!important}.link-info{color:#0dcaf0!important}.link-info:focus,.link-info:hover{color:#3dd5f3!important}.link-warning{color:#ffc107!important}.link-warning:focus,.link-warning:hover{color:#ffcd39!important}.link-danger{color:#dc3545!important}.link-danger:focus,.link-danger:hover{color:#b02a37!important}.link-light{color:#f8f9fa!important}.link-light:focus,.link-light:hover{color:#f9fafb!important}.link-dark{color:#212529!important}.link-dark:focus,.link-dark:hover{color:#1a1e21!important}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:75%}.ratio-16x9{--bs-aspect-ratio:56.25%}.ratio-21x9{--bs-aspect-ratio:42.8571428571%}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}@media (min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media (min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:1 1 auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){position:absolute!important;width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:1px;min-height:1em;background-color:currentcolor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-0{border:0!important}.border-top{border-top:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)!important}.border-start-0{border-left:0!important}.border-primary{--bs-border-opacity:1;border-color:rgba(var(--bs-primary-rgb),var(--bs-border-opacity))!important}.border-secondary{--bs-border-opacity:1;border-color:rgba(var(--bs-secondary-rgb),var(--bs-border-opacity))!important}.border-success{--bs-border-opacity:1;border-color:rgba(var(--bs-success-rgb),var(--bs-border-opacity))!important}.border-info{--bs-border-opacity:1;border-color:rgba(var(--bs-info-rgb),var(--bs-border-opacity))!important}.border-warning{--bs-border-opacity:1;border-color:rgba(var(--bs-warning-rgb),var(--bs-border-opacity))!important}.border-danger{--bs-border-opacity:1;border-color:rgba(var(--bs-danger-rgb),var(--bs-border-opacity))!important}.border-light{--bs-border-opacity:1;border-color:rgba(var(--bs-light-rgb),var(--bs-border-opacity))!important}.border-dark{--bs-border-opacity:1;border-color:rgba(var(--bs-dark-rgb),var(--bs-border-opacity))!important}.border-white{--bs-border-opacity:1;border-color:rgba(var(--bs-white-rgb),var(--bs-border-opacity))!important}.border-1{--bs-border-width:1px}.border-2{--bs-border-width:2px}.border-3{--bs-border-width:3px}.border-4{--bs-border-width:4px}.border-5{--bs-border-width:5px}.border-opacity-10{--bs-border-opacity:0.1}.border-opacity-25{--bs-border-opacity:0.25}.border-opacity-50{--bs-border-opacity:0.5}.border-opacity-75{--bs-border-opacity:0.75}.border-opacity-100{--bs-border-opacity:1}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:1 1 auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.25rem!important}.m-2{margin:.5rem!important}.m-3{margin:1rem!important}.m-4{margin:1.5rem!important}.m-5{margin:3rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-3{margin-right:1rem!important;margin-left:1rem!important}.mx-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-5{margin-right:3rem!important;margin-left:3rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.25rem!important}.mt-2{margin-top:.5rem!important}.mt-3{margin-top:1rem!important}.mt-4{margin-top:1.5rem!important}.mt-5{margin-top:3rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.25rem!important}.me-2{margin-right:.5rem!important}.me-3{margin-right:1rem!important}.me-4{margin-right:1.5rem!important}.me-5{margin-right:3rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.25rem!important}.mb-2{margin-bottom:.5rem!important}.mb-3{margin-bottom:1rem!important}.mb-4{margin-bottom:1.5rem!important}.mb-5{margin-bottom:3rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.25rem!important}.ms-2{margin-left:.5rem!important}.ms-3{margin-left:1rem!important}.ms-4{margin-left:1.5rem!important}.ms-5{margin-left:3rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.25rem!important}.p-2{padding:.5rem!important}.p-3{padding:1rem!important}.p-4{padding:1.5rem!important}.p-5{padding:3rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-3{padding-right:1rem!important;padding-left:1rem!important}.px-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-5{padding-right:3rem!important;padding-left:3rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.25rem!important}.pt-2{padding-top:.5rem!important}.pt-3{padding-top:1rem!important}.pt-4{padding-top:1.5rem!important}.pt-5{padding-top:3rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.25rem!important}.pe-2{padding-right:.5rem!important}.pe-3{padding-right:1rem!important}.pe-4{padding-right:1.5rem!important}.pe-5{padding-right:3rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.25rem!important}.pb-2{padding-bottom:.5rem!important}.pb-3{padding-bottom:1rem!important}.pb-4{padding-bottom:1.5rem!important}.pb-5{padding-bottom:3rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.25rem!important}.ps-2{padding-left:.5rem!important}.ps-3{padding-left:1rem!important}.ps-4{padding-left:1.5rem!important}.ps-5{padding-left:3rem!important}.gap-0{gap:0!important}.gap-1{gap:.25rem!important}.gap-2{gap:.5rem!important}.gap-3{gap:1rem!important}.gap-4{gap:1.5rem!important}.gap-5{gap:3rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-light{font-weight:300!important}.fw-lighter{font-weight:lighter!important}.fw-normal{font-weight:400!important}.fw-bold{font-weight:700!important}.fw-semibold{font-weight:600!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:#6c757d!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:transparent!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:var(--bs-border-radius)!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:var(--bs-border-radius-sm)!important}.rounded-2{border-radius:var(--bs-border-radius)!important}.rounded-3{border-radius:var(--bs-border-radius-lg)!important}.rounded-4{border-radius:var(--bs-border-radius-xl)!important}.rounded-5{border-radius:var(--bs-border-radius-2xl)!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:var(--bs-border-radius-pill)!important}.rounded-top{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-end{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media (min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:1 1 auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.25rem!important}.m-sm-2{margin:.5rem!important}.m-sm-3{margin:1rem!important}.m-sm-4{margin:1.5rem!important}.m-sm-5{margin:3rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-sm-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-3{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-sm-5{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-sm-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-sm-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.25rem!important}.mt-sm-2{margin-top:.5rem!important}.mt-sm-3{margin-top:1rem!important}.mt-sm-4{margin-top:1.5rem!important}.mt-sm-5{margin-top:3rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.25rem!important}.me-sm-2{margin-right:.5rem!important}.me-sm-3{margin-right:1rem!important}.me-sm-4{margin-right:1.5rem!important}.me-sm-5{margin-right:3rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.25rem!important}.mb-sm-2{margin-bottom:.5rem!important}.mb-sm-3{margin-bottom:1rem!important}.mb-sm-4{margin-bottom:1.5rem!important}.mb-sm-5{margin-bottom:3rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.25rem!important}.ms-sm-2{margin-left:.5rem!important}.ms-sm-3{margin-left:1rem!important}.ms-sm-4{margin-left:1.5rem!important}.ms-sm-5{margin-left:3rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.25rem!important}.p-sm-2{padding:.5rem!important}.p-sm-3{padding:1rem!important}.p-sm-4{padding:1.5rem!important}.p-sm-5{padding:3rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-sm-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-3{padding-right:1rem!important;padding-left:1rem!important}.px-sm-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-sm-5{padding-right:3rem!important;padding-left:3rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-sm-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-sm-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.25rem!important}.pt-sm-2{padding-top:.5rem!important}.pt-sm-3{padding-top:1rem!important}.pt-sm-4{padding-top:1.5rem!important}.pt-sm-5{padding-top:3rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.25rem!important}.pe-sm-2{padding-right:.5rem!important}.pe-sm-3{padding-right:1rem!important}.pe-sm-4{padding-right:1.5rem!important}.pe-sm-5{padding-right:3rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.25rem!important}.pb-sm-2{padding-bottom:.5rem!important}.pb-sm-3{padding-bottom:1rem!important}.pb-sm-4{padding-bottom:1.5rem!important}.pb-sm-5{padding-bottom:3rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.25rem!important}.ps-sm-2{padding-left:.5rem!important}.ps-sm-3{padding-left:1rem!important}.ps-sm-4{padding-left:1.5rem!important}.ps-sm-5{padding-left:3rem!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.25rem!important}.gap-sm-2{gap:.5rem!important}.gap-sm-3{gap:1rem!important}.gap-sm-4{gap:1.5rem!important}.gap-sm-5{gap:3rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:1 1 auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.25rem!important}.m-md-2{margin:.5rem!important}.m-md-3{margin:1rem!important}.m-md-4{margin:1.5rem!important}.m-md-5{margin:3rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-md-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-3{margin-right:1rem!important;margin-left:1rem!important}.mx-md-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-md-5{margin-right:3rem!important;margin-left:3rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-md-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-md-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.25rem!important}.mt-md-2{margin-top:.5rem!important}.mt-md-3{margin-top:1rem!important}.mt-md-4{margin-top:1.5rem!important}.mt-md-5{margin-top:3rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.25rem!important}.me-md-2{margin-right:.5rem!important}.me-md-3{margin-right:1rem!important}.me-md-4{margin-right:1.5rem!important}.me-md-5{margin-right:3rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.25rem!important}.mb-md-2{margin-bottom:.5rem!important}.mb-md-3{margin-bottom:1rem!important}.mb-md-4{margin-bottom:1.5rem!important}.mb-md-5{margin-bottom:3rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.25rem!important}.ms-md-2{margin-left:.5rem!important}.ms-md-3{margin-left:1rem!important}.ms-md-4{margin-left:1.5rem!important}.ms-md-5{margin-left:3rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.25rem!important}.p-md-2{padding:.5rem!important}.p-md-3{padding:1rem!important}.p-md-4{padding:1.5rem!important}.p-md-5{padding:3rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-md-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-3{padding-right:1rem!important;padding-left:1rem!important}.px-md-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-md-5{padding-right:3rem!important;padding-left:3rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-md-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-md-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.25rem!important}.pt-md-2{padding-top:.5rem!important}.pt-md-3{padding-top:1rem!important}.pt-md-4{padding-top:1.5rem!important}.pt-md-5{padding-top:3rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.25rem!important}.pe-md-2{padding-right:.5rem!important}.pe-md-3{padding-right:1rem!important}.pe-md-4{padding-right:1.5rem!important}.pe-md-5{padding-right:3rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.25rem!important}.pb-md-2{padding-bottom:.5rem!important}.pb-md-3{padding-bottom:1rem!important}.pb-md-4{padding-bottom:1.5rem!important}.pb-md-5{padding-bottom:3rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.25rem!important}.ps-md-2{padding-left:.5rem!important}.ps-md-3{padding-left:1rem!important}.ps-md-4{padding-left:1.5rem!important}.ps-md-5{padding-left:3rem!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.25rem!important}.gap-md-2{gap:.5rem!important}.gap-md-3{gap:1rem!important}.gap-md-4{gap:1.5rem!important}.gap-md-5{gap:3rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:1 1 auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.25rem!important}.m-lg-2{margin:.5rem!important}.m-lg-3{margin:1rem!important}.m-lg-4{margin:1.5rem!important}.m-lg-5{margin:3rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-lg-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-3{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-lg-5{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-lg-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-lg-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.25rem!important}.mt-lg-2{margin-top:.5rem!important}.mt-lg-3{margin-top:1rem!important}.mt-lg-4{margin-top:1.5rem!important}.mt-lg-5{margin-top:3rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.25rem!important}.me-lg-2{margin-right:.5rem!important}.me-lg-3{margin-right:1rem!important}.me-lg-4{margin-right:1.5rem!important}.me-lg-5{margin-right:3rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.25rem!important}.mb-lg-2{margin-bottom:.5rem!important}.mb-lg-3{margin-bottom:1rem!important}.mb-lg-4{margin-bottom:1.5rem!important}.mb-lg-5{margin-bottom:3rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.25rem!important}.ms-lg-2{margin-left:.5rem!important}.ms-lg-3{margin-left:1rem!important}.ms-lg-4{margin-left:1.5rem!important}.ms-lg-5{margin-left:3rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.25rem!important}.p-lg-2{padding:.5rem!important}.p-lg-3{padding:1rem!important}.p-lg-4{padding:1.5rem!important}.p-lg-5{padding:3rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-lg-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-3{padding-right:1rem!important;padding-left:1rem!important}.px-lg-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-lg-5{padding-right:3rem!important;padding-left:3rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-lg-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-lg-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.25rem!important}.pt-lg-2{padding-top:.5rem!important}.pt-lg-3{padding-top:1rem!important}.pt-lg-4{padding-top:1.5rem!important}.pt-lg-5{padding-top:3rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.25rem!important}.pe-lg-2{padding-right:.5rem!important}.pe-lg-3{padding-right:1rem!important}.pe-lg-4{padding-right:1.5rem!important}.pe-lg-5{padding-right:3rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.25rem!important}.pb-lg-2{padding-bottom:.5rem!important}.pb-lg-3{padding-bottom:1rem!important}.pb-lg-4{padding-bottom:1.5rem!important}.pb-lg-5{padding-bottom:3rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.25rem!important}.ps-lg-2{padding-left:.5rem!important}.ps-lg-3{padding-left:1rem!important}.ps-lg-4{padding-left:1.5rem!important}.ps-lg-5{padding-left:3rem!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.25rem!important}.gap-lg-2{gap:.5rem!important}.gap-lg-3{gap:1rem!important}.gap-lg-4{gap:1.5rem!important}.gap-lg-5{gap:3rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:1 1 auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.25rem!important}.m-xl-2{margin:.5rem!important}.m-xl-3{margin:1rem!important}.m-xl-4{margin:1.5rem!important}.m-xl-5{margin:3rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.25rem!important}.mt-xl-2{margin-top:.5rem!important}.mt-xl-3{margin-top:1rem!important}.mt-xl-4{margin-top:1.5rem!important}.mt-xl-5{margin-top:3rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.25rem!important}.me-xl-2{margin-right:.5rem!important}.me-xl-3{margin-right:1rem!important}.me-xl-4{margin-right:1.5rem!important}.me-xl-5{margin-right:3rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.25rem!important}.mb-xl-2{margin-bottom:.5rem!important}.mb-xl-3{margin-bottom:1rem!important}.mb-xl-4{margin-bottom:1.5rem!important}.mb-xl-5{margin-bottom:3rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.25rem!important}.ms-xl-2{margin-left:.5rem!important}.ms-xl-3{margin-left:1rem!important}.ms-xl-4{margin-left:1.5rem!important}.ms-xl-5{margin-left:3rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.25rem!important}.p-xl-2{padding:.5rem!important}.p-xl-3{padding:1rem!important}.p-xl-4{padding:1.5rem!important}.p-xl-5{padding:3rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.25rem!important}.pt-xl-2{padding-top:.5rem!important}.pt-xl-3{padding-top:1rem!important}.pt-xl-4{padding-top:1.5rem!important}.pt-xl-5{padding-top:3rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.25rem!important}.pe-xl-2{padding-right:.5rem!important}.pe-xl-3{padding-right:1rem!important}.pe-xl-4{padding-right:1.5rem!important}.pe-xl-5{padding-right:3rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.25rem!important}.pb-xl-2{padding-bottom:.5rem!important}.pb-xl-3{padding-bottom:1rem!important}.pb-xl-4{padding-bottom:1.5rem!important}.pb-xl-5{padding-bottom:3rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.25rem!important}.ps-xl-2{padding-left:.5rem!important}.ps-xl-3{padding-left:1rem!important}.ps-xl-4{padding-left:1.5rem!important}.ps-xl-5{padding-left:3rem!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.25rem!important}.gap-xl-2{gap:.5rem!important}.gap-xl-3{gap:1rem!important}.gap-xl-4{gap:1.5rem!important}.gap-xl-5{gap:3rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media (min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:1 1 auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.25rem!important}.m-xxl-2{margin:.5rem!important}.m-xxl-3{margin:1rem!important}.m-xxl-4{margin:1.5rem!important}.m-xxl-5{margin:3rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.25rem!important;margin-left:.25rem!important}.mx-xxl-2{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-3{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-4{margin-right:1.5rem!important;margin-left:1.5rem!important}.mx-xxl-5{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.25rem!important;margin-bottom:.25rem!important}.my-xxl-2{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-3{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-4{margin-top:1.5rem!important;margin-bottom:1.5rem!important}.my-xxl-5{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.25rem!important}.mt-xxl-2{margin-top:.5rem!important}.mt-xxl-3{margin-top:1rem!important}.mt-xxl-4{margin-top:1.5rem!important}.mt-xxl-5{margin-top:3rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.25rem!important}.me-xxl-2{margin-right:.5rem!important}.me-xxl-3{margin-right:1rem!important}.me-xxl-4{margin-right:1.5rem!important}.me-xxl-5{margin-right:3rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.25rem!important}.mb-xxl-2{margin-bottom:.5rem!important}.mb-xxl-3{margin-bottom:1rem!important}.mb-xxl-4{margin-bottom:1.5rem!important}.mb-xxl-5{margin-bottom:3rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.25rem!important}.ms-xxl-2{margin-left:.5rem!important}.ms-xxl-3{margin-left:1rem!important}.ms-xxl-4{margin-left:1.5rem!important}.ms-xxl-5{margin-left:3rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.25rem!important}.p-xxl-2{padding:.5rem!important}.p-xxl-3{padding:1rem!important}.p-xxl-4{padding:1.5rem!important}.p-xxl-5{padding:3rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.25rem!important;padding-left:.25rem!important}.px-xxl-2{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-3{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-4{padding-right:1.5rem!important;padding-left:1.5rem!important}.px-xxl-5{padding-right:3rem!important;padding-left:3rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.25rem!important;padding-bottom:.25rem!important}.py-xxl-2{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-3{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-4{padding-top:1.5rem!important;padding-bottom:1.5rem!important}.py-xxl-5{padding-top:3rem!important;padding-bottom:3rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.25rem!important}.pt-xxl-2{padding-top:.5rem!important}.pt-xxl-3{padding-top:1rem!important}.pt-xxl-4{padding-top:1.5rem!important}.pt-xxl-5{padding-top:3rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.25rem!important}.pe-xxl-2{padding-right:.5rem!important}.pe-xxl-3{padding-right:1rem!important}.pe-xxl-4{padding-right:1.5rem!important}.pe-xxl-5{padding-right:3rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.25rem!important}.pb-xxl-2{padding-bottom:.5rem!important}.pb-xxl-3{padding-bottom:1rem!important}.pb-xxl-4{padding-bottom:1.5rem!important}.pb-xxl-5{padding-bottom:3rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.25rem!important}.ps-xxl-2{padding-left:.5rem!important}.ps-xxl-3{padding-left:1rem!important}.ps-xxl-4{padding-left:1.5rem!important}.ps-xxl-5{padding-left:3rem!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.25rem!important}.gap-xxl-2{gap:.5rem!important}.gap-xxl-3{gap:1rem!important}.gap-xxl-4{gap:1.5rem!important}.gap-xxl-5{gap:3rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media (min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/gnuviechadmin/static/css/bootstrap.min.css.map b/gnuviechadmin/static/css/bootstrap.min.css.map new file mode 100644 index 0000000..336fa28 --- /dev/null +++ b/gnuviechadmin/static/css/bootstrap.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../scss/mixins/_banner.scss","../../scss/_root.scss","../../scss/vendor/_rfs.scss","../../scss/_reboot.scss","dist/css/bootstrap.css","../../scss/mixins/_border-radius.scss","../../scss/_type.scss","../../scss/mixins/_lists.scss","../../scss/_images.scss","../../scss/mixins/_image.scss","../../scss/_containers.scss","../../scss/mixins/_container.scss","../../scss/mixins/_breakpoints.scss","../../scss/_grid.scss","../../scss/mixins/_grid.scss","../../scss/_tables.scss","../../scss/mixins/_table-variants.scss","../../scss/forms/_labels.scss","../../scss/forms/_form-text.scss","../../scss/forms/_form-control.scss","../../scss/mixins/_transition.scss","../../scss/mixins/_gradients.scss","../../scss/forms/_form-select.scss","../../scss/forms/_form-check.scss","../../scss/forms/_form-range.scss","../../scss/forms/_floating-labels.scss","../../scss/forms/_input-group.scss","../../scss/mixins/_forms.scss","../../scss/_buttons.scss","../../scss/mixins/_buttons.scss","../../scss/_transitions.scss","../../scss/_dropdown.scss","../../scss/mixins/_caret.scss","../../scss/_button-group.scss","../../scss/_nav.scss","../../scss/_navbar.scss","../../scss/_card.scss","../../scss/_accordion.scss","../../scss/_breadcrumb.scss","../../scss/_pagination.scss","../../scss/mixins/_pagination.scss","../../scss/_badge.scss","../../scss/_alert.scss","../../scss/mixins/_alert.scss","../../scss/_progress.scss","../../scss/_list-group.scss","../../scss/mixins/_list-group.scss","../../scss/_close.scss","../../scss/_toasts.scss","../../scss/_modal.scss","../../scss/mixins/_backdrop.scss","../../scss/_tooltip.scss","../../scss/mixins/_reset-text.scss","../../scss/_popover.scss","../../scss/_carousel.scss","../../scss/mixins/_clearfix.scss","../../scss/_spinners.scss","../../scss/_offcanvas.scss","../../scss/_placeholders.scss","../../scss/helpers/_color-bg.scss","../../scss/helpers/_colored-links.scss","../../scss/helpers/_ratio.scss","../../scss/helpers/_position.scss","../../scss/helpers/_stacks.scss","../../scss/helpers/_visually-hidden.scss","../../scss/mixins/_visually-hidden.scss","../../scss/helpers/_stretched-link.scss","../../scss/helpers/_text-truncation.scss","../../scss/mixins/_text-truncate.scss","../../scss/helpers/_vr.scss","../../scss/mixins/_utilities.scss","../../scss/utilities/_api.scss"],"names":[],"mappings":"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","sourcesContent":["@mixin bsBanner($file) {\n /*!\n * Bootstrap #{$file} v5.2.3 (https://getbootstrap.com/)\n * Copyright 2011-2022 The Bootstrap Authors\n * Copyright 2011-2022 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n */\n}\n\n",":root {\n // Note: Custom variable values only support SassScript inside `#{}`.\n\n // Colors\n //\n // Generate palettes for full colors, grays, and theme colors.\n\n @each $color, $value in $colors {\n --#{$prefix}#{$color}: #{$value};\n }\n\n @each $color, $value in $grays {\n --#{$prefix}gray-#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors {\n --#{$prefix}#{$color}: #{$value};\n }\n\n @each $color, $value in $theme-colors-rgb {\n --#{$prefix}#{$color}-rgb: #{$value};\n }\n\n --#{$prefix}white-rgb: #{to-rgb($white)};\n --#{$prefix}black-rgb: #{to-rgb($black)};\n --#{$prefix}body-color-rgb: #{to-rgb($body-color)};\n --#{$prefix}body-bg-rgb: #{to-rgb($body-bg)};\n\n // Fonts\n\n // Note: Use `inspect` for lists so that quoted items keep the quotes.\n // See https://github.com/sass/sass/issues/2383#issuecomment-336349172\n --#{$prefix}font-sans-serif: #{inspect($font-family-sans-serif)};\n --#{$prefix}font-monospace: #{inspect($font-family-monospace)};\n --#{$prefix}gradient: #{$gradient};\n\n // Root and body\n // scss-docs-start root-body-variables\n @if $font-size-root != null {\n --#{$prefix}root-font-size: #{$font-size-root};\n }\n --#{$prefix}body-font-family: #{$font-family-base};\n @include rfs($font-size-base, --#{$prefix}body-font-size);\n --#{$prefix}body-font-weight: #{$font-weight-base};\n --#{$prefix}body-line-height: #{$line-height-base};\n --#{$prefix}body-color: #{$body-color};\n @if $body-text-align != null {\n --#{$prefix}body-text-align: #{$body-text-align};\n }\n --#{$prefix}body-bg: #{$body-bg};\n // scss-docs-end root-body-variables\n\n // scss-docs-start root-border-var\n --#{$prefix}border-width: #{$border-width};\n --#{$prefix}border-style: #{$border-style};\n --#{$prefix}border-color: #{$border-color};\n --#{$prefix}border-color-translucent: #{$border-color-translucent};\n\n --#{$prefix}border-radius: #{$border-radius};\n --#{$prefix}border-radius-sm: #{$border-radius-sm};\n --#{$prefix}border-radius-lg: #{$border-radius-lg};\n --#{$prefix}border-radius-xl: #{$border-radius-xl};\n --#{$prefix}border-radius-2xl: #{$border-radius-2xl};\n --#{$prefix}border-radius-pill: #{$border-radius-pill};\n // scss-docs-end root-border-var\n\n --#{$prefix}link-color: #{$link-color};\n --#{$prefix}link-hover-color: #{$link-hover-color};\n\n --#{$prefix}code-color: #{$code-color};\n\n --#{$prefix}highlight-bg: #{$mark-bg};\n}\n","// stylelint-disable property-blacklist, scss/dollar-variable-default\n\n// SCSS RFS mixin\n//\n// Automated responsive values for font sizes, paddings, margins and much more\n//\n// Licensed under MIT (https://github.com/twbs/rfs/blob/main/LICENSE)\n\n// Configuration\n\n// Base value\n$rfs-base-value: 1.25rem !default;\n$rfs-unit: rem !default;\n\n@if $rfs-unit != rem and $rfs-unit != px {\n @error \"`#{$rfs-unit}` is not a valid unit for $rfs-unit. Use `px` or `rem`.\";\n}\n\n// Breakpoint at where values start decreasing if screen width is smaller\n$rfs-breakpoint: 1200px !default;\n$rfs-breakpoint-unit: px !default;\n\n@if $rfs-breakpoint-unit != px and $rfs-breakpoint-unit != em and $rfs-breakpoint-unit != rem {\n @error \"`#{$rfs-breakpoint-unit}` is not a valid unit for $rfs-breakpoint-unit. Use `px`, `em` or `rem`.\";\n}\n\n// Resize values based on screen height and width\n$rfs-two-dimensional: false !default;\n\n// Factor of decrease\n$rfs-factor: 10 !default;\n\n@if type-of($rfs-factor) != number or $rfs-factor <= 1 {\n @error \"`#{$rfs-factor}` is not a valid $rfs-factor, it must be greater than 1.\";\n}\n\n// Mode. Possibilities: \"min-media-query\", \"max-media-query\"\n$rfs-mode: min-media-query !default;\n\n// Generate enable or disable classes. Possibilities: false, \"enable\" or \"disable\"\n$rfs-class: false !default;\n\n// 1 rem = $rfs-rem-value px\n$rfs-rem-value: 16 !default;\n\n// Safari iframe resize bug: https://github.com/twbs/rfs/issues/14\n$rfs-safari-iframe-resize-bug-fix: false !default;\n\n// Disable RFS by setting $enable-rfs to false\n$enable-rfs: true !default;\n\n// Cache $rfs-base-value unit\n$rfs-base-value-unit: unit($rfs-base-value);\n\n@function divide($dividend, $divisor, $precision: 10) {\n $sign: if($dividend > 0 and $divisor > 0 or $dividend < 0 and $divisor < 0, 1, -1);\n $dividend: abs($dividend);\n $divisor: abs($divisor);\n @if $dividend == 0 {\n @return 0;\n }\n @if $divisor == 0 {\n @error \"Cannot divide by 0\";\n }\n $remainder: $dividend;\n $result: 0;\n $factor: 10;\n @while ($remainder > 0 and $precision >= 0) {\n $quotient: 0;\n @while ($remainder >= $divisor) {\n $remainder: $remainder - $divisor;\n $quotient: $quotient + 1;\n }\n $result: $result * 10 + $quotient;\n $factor: $factor * .1;\n $remainder: $remainder * 10;\n $precision: $precision - 1;\n @if ($precision < 0 and $remainder >= $divisor * 5) {\n $result: $result + 1;\n }\n }\n $result: $result * $factor * $sign;\n $dividend-unit: unit($dividend);\n $divisor-unit: unit($divisor);\n $unit-map: (\n \"px\": 1px,\n \"rem\": 1rem,\n \"em\": 1em,\n \"%\": 1%\n );\n @if ($dividend-unit != $divisor-unit and map-has-key($unit-map, $dividend-unit)) {\n $result: $result * map-get($unit-map, $dividend-unit);\n }\n @return $result;\n}\n\n// Remove px-unit from $rfs-base-value for calculations\n@if $rfs-base-value-unit == px {\n $rfs-base-value: divide($rfs-base-value, $rfs-base-value * 0 + 1);\n}\n@else if $rfs-base-value-unit == rem {\n $rfs-base-value: divide($rfs-base-value, divide($rfs-base-value * 0 + 1, $rfs-rem-value));\n}\n\n// Cache $rfs-breakpoint unit to prevent multiple calls\n$rfs-breakpoint-unit-cache: unit($rfs-breakpoint);\n\n// Remove unit from $rfs-breakpoint for calculations\n@if $rfs-breakpoint-unit-cache == px {\n $rfs-breakpoint: divide($rfs-breakpoint, $rfs-breakpoint * 0 + 1);\n}\n@else if $rfs-breakpoint-unit-cache == rem or $rfs-breakpoint-unit-cache == \"em\" {\n $rfs-breakpoint: divide($rfs-breakpoint, divide($rfs-breakpoint * 0 + 1, $rfs-rem-value));\n}\n\n// Calculate the media query value\n$rfs-mq-value: if($rfs-breakpoint-unit == px, #{$rfs-breakpoint}px, #{divide($rfs-breakpoint, $rfs-rem-value)}#{$rfs-breakpoint-unit});\n$rfs-mq-property-width: if($rfs-mode == max-media-query, max-width, min-width);\n$rfs-mq-property-height: if($rfs-mode == max-media-query, max-height, min-height);\n\n// Internal mixin used to determine which media query needs to be used\n@mixin _rfs-media-query {\n @if $rfs-two-dimensional {\n @if $rfs-mode == max-media-query {\n @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}), (#{$rfs-mq-property-height}: #{$rfs-mq-value}) {\n @content;\n }\n }\n @else {\n @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}) and (#{$rfs-mq-property-height}: #{$rfs-mq-value}) {\n @content;\n }\n }\n }\n @else {\n @media (#{$rfs-mq-property-width}: #{$rfs-mq-value}) {\n @content;\n }\n }\n}\n\n// Internal mixin that adds disable classes to the selector if needed.\n@mixin _rfs-rule {\n @if $rfs-class == disable and $rfs-mode == max-media-query {\n // Adding an extra class increases specificity, which prevents the media query to override the property\n &,\n .disable-rfs &,\n &.disable-rfs {\n @content;\n }\n }\n @else if $rfs-class == enable and $rfs-mode == min-media-query {\n .enable-rfs &,\n &.enable-rfs {\n @content;\n }\n }\n @else {\n @content;\n }\n}\n\n// Internal mixin that adds enable classes to the selector if needed.\n@mixin _rfs-media-query-rule {\n\n @if $rfs-class == enable {\n @if $rfs-mode == min-media-query {\n @content;\n }\n\n @include _rfs-media-query {\n .enable-rfs &,\n &.enable-rfs {\n @content;\n }\n }\n }\n @else {\n @if $rfs-class == disable and $rfs-mode == min-media-query {\n .disable-rfs &,\n &.disable-rfs {\n @content;\n }\n }\n @include _rfs-media-query {\n @content;\n }\n }\n}\n\n// Helper function to get the formatted non-responsive value\n@function rfs-value($values) {\n // Convert to list\n $values: if(type-of($values) != list, ($values,), $values);\n\n $val: '';\n\n // Loop over each value and calculate value\n @each $value in $values {\n @if $value == 0 {\n $val: $val + ' 0';\n }\n @else {\n // Cache $value unit\n $unit: if(type-of($value) == \"number\", unit($value), false);\n\n @if $unit == px {\n // Convert to rem if needed\n $val: $val + ' ' + if($rfs-unit == rem, #{divide($value, $value * 0 + $rfs-rem-value)}rem, $value);\n }\n @else if $unit == rem {\n // Convert to px if needed\n $val: $val + ' ' + if($rfs-unit == px, #{divide($value, $value * 0 + 1) * $rfs-rem-value}px, $value);\n }\n @else {\n // If $value isn't a number (like inherit) or $value has a unit (not px or rem, like 1.5em) or $ is 0, just print the value\n $val: $val + ' ' + $value;\n }\n }\n }\n\n // Remove first space\n @return unquote(str-slice($val, 2));\n}\n\n// Helper function to get the responsive value calculated by RFS\n@function rfs-fluid-value($values) {\n // Convert to list\n $values: if(type-of($values) != list, ($values,), $values);\n\n $val: '';\n\n // Loop over each value and calculate value\n @each $value in $values {\n @if $value == 0 {\n $val: $val + ' 0';\n }\n\n @else {\n // Cache $value unit\n $unit: if(type-of($value) == \"number\", unit($value), false);\n\n // If $value isn't a number (like inherit) or $value has a unit (not px or rem, like 1.5em) or $ is 0, just print the value\n @if not $unit or $unit != px and $unit != rem {\n $val: $val + ' ' + $value;\n }\n\n @else {\n // Remove unit from $value for calculations\n $value: divide($value, $value * 0 + if($unit == px, 1, divide(1, $rfs-rem-value)));\n\n // Only add the media query if the value is greater than the minimum value\n @if abs($value) <= $rfs-base-value or not $enable-rfs {\n $val: $val + ' ' + if($rfs-unit == rem, #{divide($value, $rfs-rem-value)}rem, #{$value}px);\n }\n @else {\n // Calculate the minimum value\n $value-min: $rfs-base-value + divide(abs($value) - $rfs-base-value, $rfs-factor);\n\n // Calculate difference between $value and the minimum value\n $value-diff: abs($value) - $value-min;\n\n // Base value formatting\n $min-width: if($rfs-unit == rem, #{divide($value-min, $rfs-rem-value)}rem, #{$value-min}px);\n\n // Use negative value if needed\n $min-width: if($value < 0, -$min-width, $min-width);\n\n // Use `vmin` if two-dimensional is enabled\n $variable-unit: if($rfs-two-dimensional, vmin, vw);\n\n // Calculate the variable width between 0 and $rfs-breakpoint\n $variable-width: #{divide($value-diff * 100, $rfs-breakpoint)}#{$variable-unit};\n\n // Return the calculated value\n $val: $val + ' calc(' + $min-width + if($value < 0, ' - ', ' + ') + $variable-width + ')';\n }\n }\n }\n }\n\n // Remove first space\n @return unquote(str-slice($val, 2));\n}\n\n// RFS mixin\n@mixin rfs($values, $property: font-size) {\n @if $values != null {\n $val: rfs-value($values);\n $fluidVal: rfs-fluid-value($values);\n\n // Do not print the media query if responsive & non-responsive values are the same\n @if $val == $fluidVal {\n #{$property}: $val;\n }\n @else {\n @include _rfs-rule {\n #{$property}: if($rfs-mode == max-media-query, $val, $fluidVal);\n\n // Include safari iframe resize fix if needed\n min-width: if($rfs-safari-iframe-resize-bug-fix, (0 * 1vw), null);\n }\n\n @include _rfs-media-query-rule {\n #{$property}: if($rfs-mode == max-media-query, $fluidVal, $val);\n }\n }\n }\n}\n\n// Shorthand helper mixins\n@mixin font-size($value) {\n @include rfs($value);\n}\n\n@mixin padding($value) {\n @include rfs($value, padding);\n}\n\n@mixin padding-top($value) {\n @include rfs($value, padding-top);\n}\n\n@mixin padding-right($value) {\n @include rfs($value, padding-right);\n}\n\n@mixin padding-bottom($value) {\n @include rfs($value, padding-bottom);\n}\n\n@mixin padding-left($value) {\n @include rfs($value, padding-left);\n}\n\n@mixin margin($value) {\n @include rfs($value, margin);\n}\n\n@mixin margin-top($value) {\n @include rfs($value, margin-top);\n}\n\n@mixin margin-right($value) {\n @include rfs($value, margin-right);\n}\n\n@mixin margin-bottom($value) {\n @include rfs($value, margin-bottom);\n}\n\n@mixin margin-left($value) {\n @include rfs($value, margin-left);\n}\n","// stylelint-disable declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n\n// Root\n//\n// Ability to the value of the root font sizes, affecting the value of `rem`.\n// null by default, thus nothing is generated.\n\n:root {\n @if $font-size-root != null {\n @include font-size(var(--#{$prefix}root-font-size));\n }\n\n @if $enable-smooth-scroll {\n @media (prefers-reduced-motion: no-preference) {\n scroll-behavior: smooth;\n }\n }\n}\n\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Prevent adjustments of font size after orientation changes in iOS.\n// 4. Change the default tap highlight to be completely transparent in iOS.\n\n// scss-docs-start reboot-body-rules\nbody {\n margin: 0; // 1\n font-family: var(--#{$prefix}body-font-family);\n @include font-size(var(--#{$prefix}body-font-size));\n font-weight: var(--#{$prefix}body-font-weight);\n line-height: var(--#{$prefix}body-line-height);\n color: var(--#{$prefix}body-color);\n text-align: var(--#{$prefix}body-text-align);\n background-color: var(--#{$prefix}body-bg); // 2\n -webkit-text-size-adjust: 100%; // 3\n -webkit-tap-highlight-color: rgba($black, 0); // 4\n}\n// scss-docs-end reboot-body-rules\n\n\n// Content grouping\n//\n// 1. Reset Firefox's gray color\n\nhr {\n margin: $hr-margin-y 0;\n color: $hr-color; // 1\n border: 0;\n border-top: $hr-border-width solid $hr-border-color;\n opacity: $hr-opacity;\n}\n\n\n// Typography\n//\n// 1. Remove top margins from headings\n// By default, `

`-`

` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n\n%heading {\n margin-top: 0; // 1\n margin-bottom: $headings-margin-bottom;\n font-family: $headings-font-family;\n font-style: $headings-font-style;\n font-weight: $headings-font-weight;\n line-height: $headings-line-height;\n color: $headings-color;\n}\n\nh1 {\n @extend %heading;\n @include font-size($h1-font-size);\n}\n\nh2 {\n @extend %heading;\n @include font-size($h2-font-size);\n}\n\nh3 {\n @extend %heading;\n @include font-size($h3-font-size);\n}\n\nh4 {\n @extend %heading;\n @include font-size($h4-font-size);\n}\n\nh5 {\n @extend %heading;\n @include font-size($h5-font-size);\n}\n\nh6 {\n @extend %heading;\n @include font-size($h6-font-size);\n}\n\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\n\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n\n// Abbreviations\n//\n// 1. Add the correct text decoration in Chrome, Edge, Opera, and Safari.\n// 2. Add explicit cursor to indicate changed behavior.\n// 3. Prevent the text-decoration to be skipped.\n\nabbr[title] {\n text-decoration: underline dotted; // 1\n cursor: help; // 2\n text-decoration-skip-ink: none; // 3\n}\n\n\n// Address\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\n\n// Lists\n\nol,\nul {\n padding-left: 2rem;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\n// 1. Undo browser default\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // 1\n}\n\n\n// Blockquote\n\nblockquote {\n margin: 0 0 1rem;\n}\n\n\n// Strong\n//\n// Add the correct font weight in Chrome, Edge, and Safari\n\nb,\nstrong {\n font-weight: $font-weight-bolder;\n}\n\n\n// Small\n//\n// Add the correct font size in all browsers\n\nsmall {\n @include font-size($small-font-size);\n}\n\n\n// Mark\n\nmark {\n padding: $mark-padding;\n background-color: var(--#{$prefix}highlight-bg);\n}\n\n\n// Sub and Sup\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n\nsub,\nsup {\n position: relative;\n @include font-size($sub-sup-font-size);\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n// Links\n\na {\n color: var(--#{$prefix}link-color);\n text-decoration: $link-decoration;\n\n &:hover {\n color: var(--#{$prefix}link-hover-color);\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([class]) {\n &,\n &:hover {\n color: inherit;\n text-decoration: none;\n }\n}\n\n\n// Code\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-code;\n @include font-size(1em); // Correct the odd `em` font sizing in all browsers.\n}\n\n// 1. Remove browser default top margin\n// 2. Reset browser default of `1em` to use `rem`s\n// 3. Don't allow content to break outside\n\npre {\n display: block;\n margin-top: 0; // 1\n margin-bottom: 1rem; // 2\n overflow: auto; // 3\n @include font-size($code-font-size);\n color: $pre-color;\n\n // Account for some code outputs that place code tags in pre tags\n code {\n @include font-size(inherit);\n color: inherit;\n word-break: normal;\n }\n}\n\ncode {\n @include font-size($code-font-size);\n color: var(--#{$prefix}code-color);\n word-wrap: break-word;\n\n // Streamline the style when inside anchors to avoid broken underline and more\n a > & {\n color: inherit;\n }\n}\n\nkbd {\n padding: $kbd-padding-y $kbd-padding-x;\n @include font-size($kbd-font-size);\n color: $kbd-color;\n background-color: $kbd-bg;\n @include border-radius($border-radius-sm);\n\n kbd {\n padding: 0;\n @include font-size(1em);\n font-weight: $nested-kbd-font-weight;\n }\n}\n\n\n// Figures\n//\n// Apply a consistent margin strategy (matches our type styles).\n\nfigure {\n margin: 0 0 1rem;\n}\n\n\n// Images and content\n\nimg,\nsvg {\n vertical-align: middle;\n}\n\n\n// Tables\n//\n// Prevent double borders\n\ntable {\n caption-side: bottom;\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: $table-cell-padding-y;\n padding-bottom: $table-cell-padding-y;\n color: $table-caption-color;\n text-align: left;\n}\n\n// 1. Removes font-weight bold by inheriting\n// 2. Matches default `` alignment by inheriting `text-align`.\n// 3. Fix alignment for Safari\n\nth {\n font-weight: $table-th-font-weight; // 1\n text-align: inherit; // 2\n text-align: -webkit-match-parent; // 3\n}\n\nthead,\ntbody,\ntfoot,\ntr,\ntd,\nth {\n border-color: inherit;\n border-style: solid;\n border-width: 0;\n}\n\n\n// Forms\n//\n// 1. Allow labels to use `margin` for spacing.\n\nlabel {\n display: inline-block; // 1\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n// See https://github.com/twbs/bootstrap/issues/24093\n\nbutton {\n // stylelint-disable-next-line property-disallowed-list\n border-radius: 0;\n}\n\n// Explicitly remove focus outline in Chromium when it shouldn't be\n// visible (e.g. as result of mouse click or touch tap). It already\n// should be doing this automatically, but seems to currently be\n// confused and applies its very visible two-tone outline anyway.\n\nbutton:focus:not(:focus-visible) {\n outline: 0;\n}\n\n// 1. Remove the margin in Firefox and Safari\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // 1\n font-family: inherit;\n @include font-size(inherit);\n line-height: inherit;\n}\n\n// Remove the inheritance of text transform in Firefox\nbutton,\nselect {\n text-transform: none;\n}\n// Set the cursor for non-` - - -

- -{% else %} -

{% trans 'Warning:'%} {% trans "You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc." %}

-{% endif %} + {% if user.emailaddress_set.all %} +

{% translate 'The following e-mail addresses are associated with your account:' %}

+ + {% else %} +

+ {% translate 'Warning:' %} {% translate "You currently do not have any e-mail address set up. You should really add an e-mail address so you can receive notifications, reset your password, etc." %} +

+ {% endif %} -

{% trans "Add E-mail Address" %}

+

{% translate "Add E-mail Address" %}

-
- {% csrf_token %} - {{ form | crispy }} - -
+
+ {% csrf_token %} + {{ form | crispy }} + +
{% endblock content %} {% block extra_js %} - + {% endblock extra_js %} diff --git a/gnuviechadmin/templates/account/email/email_confirmation_message.txt b/gnuviechadmin/templates/account/email/email_confirmation_message.txt index 50bfb87..0d2257f 100644 --- a/gnuviechadmin/templates/account/email/email_confirmation_message.txt +++ b/gnuviechadmin/templates/account/email/email_confirmation_message.txt @@ -1,4 +1,4 @@ -{% load account %}{% user_display user as user_display %}{% load i18n %}{% autoescape off %}{% blocktrans with current_site.name as site_name %}User {{ user_display }} at {{ site_name }} has given this as an email address. +{% load account %}{% user_display user as user_display %}{% load i18n %}{% autoescape off %}{% blocktranslate with current_site.name as site_name %}User {{ user_display }} at {{ site_name }} has given this as an email address. To confirm this is correct, go to {{ activate_url }} -{% endblocktrans %}{% endautoescape %} +{% endblocktranslate %}{% endautoescape %} diff --git a/gnuviechadmin/templates/account/email/email_confirmation_subject.txt b/gnuviechadmin/templates/account/email/email_confirmation_subject.txt index 3c960da..0379723 100644 --- a/gnuviechadmin/templates/account/email/email_confirmation_subject.txt +++ b/gnuviechadmin/templates/account/email/email_confirmation_subject.txt @@ -1,4 +1,4 @@ {% load i18n %} {% autoescape off %} -{% blocktrans %}Confirm E-mail Address{% endblocktrans %} +{% blocktranslate %}Confirm E-mail Address{% endblocktranslate %} {% endautoescape %} diff --git a/gnuviechadmin/templates/account/email/password_reset_key_message.txt b/gnuviechadmin/templates/account/email/password_reset_key_message.txt index 8ef754a..53cbd38 100644 --- a/gnuviechadmin/templates/account/email/password_reset_key_message.txt +++ b/gnuviechadmin/templates/account/email/password_reset_key_message.txt @@ -1,8 +1,8 @@ -{% load i18n %}{% blocktrans with site.domain as site_domain %}You're receiving this e-mail because you or someone else has requested a password for your user account at {{site_domain}}. -It can be safely ignored if you did not request a password reset. Click the link below to reset your password.{% endblocktrans %} +{% load i18n %}{% blocktranslate with site.domain as site_domain %}You're receiving this e-mail because you or someone else has requested a password for your user account at {{site_domain}}. +It can be safely ignored if you did not request a password reset. Click the link below to reset your password.{% endblocktranslate %} {{ password_reset_url }} -{% if username %}{% blocktrans %}In case you forgot, your username is {{ username }}.{% endblocktrans %} +{% if username %}{% blocktranslate %}In case you forgot, your username is {{ username }}.{% endblocktranslate %} -{% endif %}{% trans 'Thanks for using our site!' %} +{% endif %}{% translate 'Thanks for using our site!' %} diff --git a/gnuviechadmin/templates/account/email/password_reset_key_subject.txt b/gnuviechadmin/templates/account/email/password_reset_key_subject.txt index 6840c40..054531a 100644 --- a/gnuviechadmin/templates/account/email/password_reset_key_subject.txt +++ b/gnuviechadmin/templates/account/email/password_reset_key_subject.txt @@ -1,4 +1,4 @@ {% load i18n %} {% autoescape off %} -{% blocktrans %}Password Reset E-mail{% endblocktrans %} +{% blocktranslate %}Password Reset E-mail{% endblocktranslate %} {% endautoescape %} diff --git a/gnuviechadmin/templates/account/email_confirm.html b/gnuviechadmin/templates/account/email_confirm.html index 80d4aef..e70246c 100644 --- a/gnuviechadmin/templates/account/email_confirm.html +++ b/gnuviechadmin/templates/account/email_confirm.html @@ -1,18 +1,21 @@ {% extends "account/base.html" %} {% load account i18n %} -{% block title %}{{ block.super }} - {% trans "Confirm E-mail Address" %}{% endblock title %} -{% block page_title %}{% trans "Confirm E-Mail Address" %}{% endblock %} +{% block title %}{{ block.super }} - {% translate "Confirm E-mail Address" %}{% endblock title %} +{% block page_title %}{% translate "Confirm E-Mail Address" %}{% endblock %} {% block content %} -{% if confirmation %} - {% user_display confirmation.email_address.user as user_display %} -

{% blocktrans with confirmation.email_address.email as email %}Please confirm that {{ email }} is an e-mail address for user {{ user_display }}.{% endblocktrans %}

-
- {% csrf_token %} - -
-{% else %} - {% url 'account_email' as email_url %} -

{% blocktrans %}This e-mail confirmation link expired or is invalid. Please issue a new e-mail confirmation request.{% endblocktrans %}

-{% endif %} + {% if confirmation %} + {% user_display confirmation.email_address.user as user_display %} +

{% blocktranslate with confirmation.email_address.email as email trimmed %}Please confirm that + {{ email }} is an e-mail address for user {{ user_display }}. + {% endblocktranslate %}

+
+ {% csrf_token %} + +
+ {% else %} + {% url 'account_email' as email_url %} +

{% blocktranslate trimmed %}This e-mail confirmation link expired or is invalid. Please + issue a new e-mail confirmation request.{% endblocktranslate %}

+ {% endif %} {% endblock content %} diff --git a/gnuviechadmin/templates/account/email_confirmed.html b/gnuviechadmin/templates/account/email_confirmed.html index a6b70bb..7f466c2 100644 --- a/gnuviechadmin/templates/account/email_confirmed.html +++ b/gnuviechadmin/templates/account/email_confirmed.html @@ -1,10 +1,12 @@ {% extends "account/base.html" %} {% load account i18n %} -{% block title %}{{ block.super }} - {% trans "Confirm E-mail Address" %}{% endblock title %} -{% block page_title %}{% trans "Confirm E-mail Address" %}{% endblock page_title %} +{% block title %}{{ block.super }} - {% translate "Confirm E-mail Address" %}{% endblock title %} +{% block page_title %}{% translate "Confirm E-mail Address" %}{% endblock page_title %} {% block content %} -{% user_display confirmation.email_address.user as user_display %} -

{% blocktrans with confirmation.email_address.email as email %}You have confirmed that {{ email }} is an e-mail address for user {{ user_display }}.{% endblocktrans %}

+ {% user_display confirmation.email_address.user as user_display %} +

{% blocktranslate with confirmation.email_address.email as email trimmed %}You have confirmed that + {{ email }} is an e-mail address for user {{ user_display }}. + {% endblocktranslate %}

{% endblock content %} diff --git a/gnuviechadmin/templates/account/login.html b/gnuviechadmin/templates/account/login.html index 88862b6..bbc211d 100644 --- a/gnuviechadmin/templates/account/login.html +++ b/gnuviechadmin/templates/account/login.html @@ -1,36 +1,33 @@ {% extends "account/base.html" %} {% load account socialaccount crispy_forms_tags i18n %} -{% block title %}{{ block.super }} - {% trans "Sign In" %}{% endblock title %} -{% block page_title %}{% trans "Sign In" %}{% endblock page_title %} +{% block title %}{{ block.super }} - {% translate "Sign In" %}{% endblock title %} +{% block page_title %}{% translate "Sign In" %}{% endblock page_title %} {% block content %} -{% get_providers as socialaccount_providers %} -{% if socialaccount_providers %} -

{% blocktrans with site.name as site_name %}Please sign in with one -of your existing third party accounts. Or, sign up -for a {{site_name}} account and sign in below:{% endblocktrans %}

- -
    - {% include "socialaccount/snippets/provider_list.html" with process="login" %} -
-

{% trans 'or' %}

-{% else %} -

{% blocktrans %}If you have not created an account yet, then please -sign up first.{% endblocktrans %}

-{% endif %} - - + {% get_providers as socialaccount_providers %} +
+
+ +
+
+ {% if socialaccount_providers %} +
    + {% include "socialaccount/snippets/provider_list.html" with process="login" %} +
+ {% endif %} +
+
{% endblock %} {% block extra_js %} -{% include "socialaccount/snippets/login_extra.html" %} + {% include "socialaccount/snippets/login_extra.html" %} {% endblock extra_js %} diff --git a/gnuviechadmin/templates/account/logout.html b/gnuviechadmin/templates/account/logout.html index 22f66de..e378bfe 100644 --- a/gnuviechadmin/templates/account/logout.html +++ b/gnuviechadmin/templates/account/logout.html @@ -1,17 +1,17 @@ {% extends "account/base.html" %} {% load i18n %} -{% block title %}{{ block.super }} - {% trans "Sign Out" %}{% endblock title %} -{% block page_title %}{% trans "Sign Out" %}{% endblock page_title %} +{% block title %}{{ block.super }} - {% translate "Sign Out" %}{% endblock title %} +{% block page_title %}{% translate "Sign Out" %}{% endblock page_title %} {% block content %} -

{% trans 'Are you sure you want to sign out?' %}

+

{% translate 'Are you sure you want to sign out?' %}

{% csrf_token %} {% if redirect_field_value %} {% endif %} - +
{% endblock %} diff --git a/gnuviechadmin/templates/account/messages/cannot_delete_primary_email.txt b/gnuviechadmin/templates/account/messages/cannot_delete_primary_email.txt index de55571..67ad1f4 100644 --- a/gnuviechadmin/templates/account/messages/cannot_delete_primary_email.txt +++ b/gnuviechadmin/templates/account/messages/cannot_delete_primary_email.txt @@ -1,2 +1,2 @@ {% load i18n %} -{% blocktrans %}You cannot remove your primary e-mail address ({{email}}).{% endblocktrans %} +{% blocktranslate %}You cannot remove your primary e-mail address ({{email}}).{% endblocktranslate %} diff --git a/gnuviechadmin/templates/account/messages/email_confirmation_sent.txt b/gnuviechadmin/templates/account/messages/email_confirmation_sent.txt index 7a526f8..fbbce2b 100644 --- a/gnuviechadmin/templates/account/messages/email_confirmation_sent.txt +++ b/gnuviechadmin/templates/account/messages/email_confirmation_sent.txt @@ -1,2 +1,2 @@ {% load i18n %} -{% blocktrans %}Confirmation e-mail sent to {{email}}.{% endblocktrans %} +{% blocktranslate %}Confirmation e-mail sent to {{email}}.{% endblocktranslate %} diff --git a/gnuviechadmin/templates/account/messages/email_confirmed.txt b/gnuviechadmin/templates/account/messages/email_confirmed.txt index 3427a4d..85aff66 100644 --- a/gnuviechadmin/templates/account/messages/email_confirmed.txt +++ b/gnuviechadmin/templates/account/messages/email_confirmed.txt @@ -1,2 +1,2 @@ {% load i18n %} -{% blocktrans %}You have confirmed {{email}}.{% endblocktrans %} +{% blocktranslate %}You have confirmed {{email}}.{% endblocktranslate %} diff --git a/gnuviechadmin/templates/account/messages/email_deleted.txt b/gnuviechadmin/templates/account/messages/email_deleted.txt index 5cf7cf9..174ce10 100644 --- a/gnuviechadmin/templates/account/messages/email_deleted.txt +++ b/gnuviechadmin/templates/account/messages/email_deleted.txt @@ -1,2 +1,2 @@ {% load i18n %} -{% blocktrans %}Removed e-mail address {{email}}.{% endblocktrans %} +{% blocktranslate %}Removed e-mail address {{email}}.{% endblocktranslate %} diff --git a/gnuviechadmin/templates/account/messages/logged_in.txt b/gnuviechadmin/templates/account/messages/logged_in.txt index f49248a..ea1abb4 100644 --- a/gnuviechadmin/templates/account/messages/logged_in.txt +++ b/gnuviechadmin/templates/account/messages/logged_in.txt @@ -1,4 +1,4 @@ {% load account %} {% load i18n %} {% user_display user as name %} -{% blocktrans %}Successfully signed in as {{name}}.{% endblocktrans %} +{% blocktranslate %}Successfully signed in as {{name}}.{% endblocktranslate %} diff --git a/gnuviechadmin/templates/account/messages/logged_out.txt b/gnuviechadmin/templates/account/messages/logged_out.txt index 2cd4627..7c424a4 100644 --- a/gnuviechadmin/templates/account/messages/logged_out.txt +++ b/gnuviechadmin/templates/account/messages/logged_out.txt @@ -1,2 +1,2 @@ {% load i18n %} -{% blocktrans %}You have signed out.{% endblocktrans %} +{% blocktranslate %}You have signed out.{% endblocktranslate %} diff --git a/gnuviechadmin/templates/account/messages/password_changed.txt b/gnuviechadmin/templates/account/messages/password_changed.txt index bd5801c..5c4a18e 100644 --- a/gnuviechadmin/templates/account/messages/password_changed.txt +++ b/gnuviechadmin/templates/account/messages/password_changed.txt @@ -1,2 +1,2 @@ {% load i18n %} -{% blocktrans %}Password successfully changed.{% endblocktrans %} +{% blocktranslate %}Password successfully changed.{% endblocktranslate %} diff --git a/gnuviechadmin/templates/account/messages/password_set.txt b/gnuviechadmin/templates/account/messages/password_set.txt index e36cef8..3cfc44b 100644 --- a/gnuviechadmin/templates/account/messages/password_set.txt +++ b/gnuviechadmin/templates/account/messages/password_set.txt @@ -1,3 +1,3 @@ {% load i18n %} -{% blocktrans %}Password successfully set.{% endblocktrans %} +{% blocktranslate %}Password successfully set.{% endblocktranslate %} diff --git a/gnuviechadmin/templates/account/messages/primary_email_set.txt b/gnuviechadmin/templates/account/messages/primary_email_set.txt index b6a70dd..54f1bf7 100644 --- a/gnuviechadmin/templates/account/messages/primary_email_set.txt +++ b/gnuviechadmin/templates/account/messages/primary_email_set.txt @@ -1,2 +1,2 @@ {% load i18n %} -{% blocktrans %}Primary e-mail address set.{% endblocktrans %} +{% blocktranslate %}Primary e-mail address set.{% endblocktranslate %} diff --git a/gnuviechadmin/templates/account/messages/unverified_primary_email.txt b/gnuviechadmin/templates/account/messages/unverified_primary_email.txt index 9c9d0d8..84ad361 100644 --- a/gnuviechadmin/templates/account/messages/unverified_primary_email.txt +++ b/gnuviechadmin/templates/account/messages/unverified_primary_email.txt @@ -1,2 +1,2 @@ {% load i18n %} -{% blocktrans %}Your primary e-mail address must be verified.{% endblocktrans %} +{% blocktranslate %}Your primary e-mail address must be verified.{% endblocktranslate %} diff --git a/gnuviechadmin/templates/account/password_change.html b/gnuviechadmin/templates/account/password_change.html index 86e153a..cee315b 100644 --- a/gnuviechadmin/templates/account/password_change.html +++ b/gnuviechadmin/templates/account/password_change.html @@ -1,13 +1,13 @@ {% extends "account/base.html" %} {% load i18n crispy_forms_tags %} -{% block title %}{{ block.super }} - {% trans "Change Password" %}{% endblock title %} -{% block page_title %}{% trans "Change Password" %}{% endblock page_title %} +{% block title %}{{ block.super }} - {% translate "Change Password" %}{% endblock title %} +{% block page_title %}{% translate "Change Password" %}{% endblock page_title %} {% block content %}
{% csrf_token %} {{ form | crispy }} - +
{% endblock %} diff --git a/gnuviechadmin/templates/account/password_reset.html b/gnuviechadmin/templates/account/password_reset.html index 90f15f1..5087e67 100644 --- a/gnuviechadmin/templates/account/password_reset.html +++ b/gnuviechadmin/templates/account/password_reset.html @@ -1,21 +1,21 @@ {% extends "account/base.html" %} {% load account i18n crispy_forms_tags %} -{% block title %}{{ block.super }} - {% trans "Password Reset" %}{% endblock title %} -{% block page_title %}{% trans "Password Reset" %}{% endblock page_title %} +{% block title %}{{ block.super }} - {% translate "Password Reset" %}{% endblock title %} +{% block page_title %}{% translate "Password Reset" %}{% endblock page_title %} {% block content %} - {% if user.is_authenticated %} - {% include "account/snippets/already_logged_in.html" %} - {% endif %} + {% if user.is_authenticated %} + {% include "account/snippets/already_logged_in.html" %} + {% endif %} -

{% trans "Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it." %}

+

{% translate "Forgotten your password? Enter your e-mail address below, and we'll send you an e-mail allowing you to reset it." %}

-
- {% csrf_token %} - {{ form | crispy }} - -
+
+ {% csrf_token %} + {{ form | crispy }} + +
-

{% blocktrans %}Please contact us if you have any trouble resetting your password.{% endblocktrans %}

+

{% blocktranslate trimmed %}Please contact us if you have any trouble resetting your password.{% endblocktranslate %}

{% endblock %} diff --git a/gnuviechadmin/templates/account/password_reset_done.html b/gnuviechadmin/templates/account/password_reset_done.html index 000ebae..940f219 100644 --- a/gnuviechadmin/templates/account/password_reset_done.html +++ b/gnuviechadmin/templates/account/password_reset_done.html @@ -1,12 +1,13 @@ {% extends "account/base.html" %} {% load account i18n %} -{% block title %}{{ block.super }} - {% trans "Password Reset" %}{% endblock title %} -{% block page_title %}{% trans "Password Reset" %}{% endblock page_title %} +{% block title %}{{ block.super }} - {% translate "Password Reset" %}{% endblock title %} +{% block page_title %}{% translate "Password Reset" %}{% endblock page_title %} {% block content %} -{% if user.is_authenticated %} - {% include "account/snippets/already_logged_in.html" %} -{% endif %} -

{% blocktrans %}We have sent you an e-mail. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}

+ {% if user.is_authenticated %} + {% include "account/snippets/already_logged_in.html" %} + {% endif %} +

{% blocktranslate trimmed %}We have sent you an e-mail. Please contact us if you do not receive it within a few + minutes.{% endblocktranslate %}

{% endblock %} diff --git a/gnuviechadmin/templates/account/password_reset_from_key.html b/gnuviechadmin/templates/account/password_reset_from_key.html index e4dd274..146171c 100644 --- a/gnuviechadmin/templates/account/password_reset_from_key.html +++ b/gnuviechadmin/templates/account/password_reset_from_key.html @@ -1,21 +1,23 @@ {% extends "account/base.html" %} {% load i18n crispy_forms_tags %} -{% block title %}{{ block.super }} - {% trans "Change Password" %}{% endblock title %} -{% block page_title %}{% if token_fail %}{% trans "Bad Token" %}{% else %}{% trans "Change Password" %}{% endif %}{% endblock page_title %} +{% block title %}{{ block.super }} - {% translate "Change Password" %}{% endblock title %} +{% block page_title %}{% if token_fail %}{% translate "Bad Token" %}{% else %}{% translate "Change Password" %} +{% endif %}{% endblock page_title %} {% block content %} -{% if token_fail %} - {% url 'account_reset_password' as passwd_reset_url %} -

{% blocktrans %}The password reset link was invalid, possibly because it has already been used. Please request a new password reset.{% endblocktrans %}

-{% else %} - {% if form %} -
- {% csrf_token %} - {{ form | crispy }} - -
- {% else %} -

{% trans 'Your password is now changed.' %}

- {% endif %} -{% endif %} + {% if token_fail %} + {% url 'account_reset_password' as passwd_reset_url %} +

{% blocktranslate trimmed %}The password reset link was invalid, possibly because it has already been used. + Please request a new password reset.{% endblocktranslate %}

+ {% else %} + {% if form %} +
+ {% csrf_token %} + {{ form | crispy }} + +
+ {% else %} +

{% translate 'Your password is now changed.' %}

+ {% endif %} + {% endif %} {% endblock content %} diff --git a/gnuviechadmin/templates/account/password_reset_from_key_done.html b/gnuviechadmin/templates/account/password_reset_from_key_done.html index d3bfabe..ffcf2ef 100644 --- a/gnuviechadmin/templates/account/password_reset_from_key_done.html +++ b/gnuviechadmin/templates/account/password_reset_from_key_done.html @@ -1,8 +1,8 @@ {% extends "account/base.html" %} {% load i18n %} -{% block title %}{{ block.super }} - {% trans "Change Password" %}{% endblock title %} -{% block page_title %}{% trans "Change Password" %}{% endblock page_title %} +{% block title %}{{ block.super }} - {% translate "Change Password" %}{% endblock title %} +{% block page_title %}{% translate "Change Password" %}{% endblock page_title %} {% block content %} -

{% trans 'Your password is now changed.' %}

+

{% translate 'Your password is now changed.' %}

{% endblock %} diff --git a/gnuviechadmin/templates/account/password_set.html b/gnuviechadmin/templates/account/password_set.html index 3bd0759..5d67e6c 100644 --- a/gnuviechadmin/templates/account/password_set.html +++ b/gnuviechadmin/templates/account/password_set.html @@ -1,13 +1,13 @@ {% extends "account/base.html" %} {% load i18n crispy_forms_tags %} -{% block title %}{{ block.super }} - {% trans "Set Password" %}{% endblock title %} -{% block page_title %}{% trans "Set Password" %}{% endblock page_title %} +{% block title %}{{ block.super }} - {% translate "Set Password" %}{% endblock title %} +{% block page_title %}{% translate "Set Password" %}{% endblock page_title %} {% block content %}
{% csrf_token %} {{ form | crispy }} - +
{% endblock %} diff --git a/gnuviechadmin/templates/account/signup.html b/gnuviechadmin/templates/account/signup.html index 4f4abf5..9be91ef 100644 --- a/gnuviechadmin/templates/account/signup.html +++ b/gnuviechadmin/templates/account/signup.html @@ -1,19 +1,21 @@ {% extends "account/base.html" %} {% load i18n crispy_forms_tags %} -{% block title %}{{ block.super }} - {% trans "Signup" %}{% endblock title %} -{% block page_title %}{% trans "Sign Up" %}{% endblock page_title %} +{% block title %}{{ block.super }} - {% translate "Signup" %}{% endblock title %} +{% block page_title %}{% translate "Sign Up" %}{% endblock page_title %} {% block content %} -

{% blocktrans %}Already have an account? Then please sign in.{% endblocktrans %}

- +

{% blocktranslate trimmed %} + Already have an account? Then please sign in. + {% endblocktranslate %}

+ {% endblock %} diff --git a/gnuviechadmin/templates/account/signup_closed.html b/gnuviechadmin/templates/account/signup_closed.html index c40efe5..4406b8f 100644 --- a/gnuviechadmin/templates/account/signup_closed.html +++ b/gnuviechadmin/templates/account/signup_closed.html @@ -1,9 +1,9 @@ {% extends "account/base.html" %} {% load i18n %} -{% block title %}{{ block.super }} - {% trans "Sign Up Closed" %}{% endblock title %} -{% block page_title %}{% trans "Sign Up Closed" %}{% endblock page_title %} +{% block title %}{{ block.super }} - {% translate "Sign Up Closed" %}{% endblock title %} +{% block page_title %}{% translate "Sign Up Closed" %}{% endblock page_title %} {% block content %} -

{% trans "We are sorry, but the sign up is currently closed." %}

+

{% translate "We are sorry, but the sign up is currently closed." %}

{% endblock %} diff --git a/gnuviechadmin/templates/account/snippets/already_logged_in.html b/gnuviechadmin/templates/account/snippets/already_logged_in.html index fd4db27..da6dfe5 100644 --- a/gnuviechadmin/templates/account/snippets/already_logged_in.html +++ b/gnuviechadmin/templates/account/snippets/already_logged_in.html @@ -1,4 +1,6 @@ {% load account i18n %} {% user_display user as user_display %} -

{% trans "Note" %}: {% blocktrans %}you are already logged in as {{ user_display }}.{% endblocktrans %}

+

{% translate "Note" %}: {% blocktranslate trimmed %} + you are already logged in as {{ user_display }}. +{% endblocktranslate %}

diff --git a/gnuviechadmin/templates/account/verification_sent.html b/gnuviechadmin/templates/account/verification_sent.html index 1811d9e..0e27797 100644 --- a/gnuviechadmin/templates/account/verification_sent.html +++ b/gnuviechadmin/templates/account/verification_sent.html @@ -2,9 +2,12 @@ {% load i18n %} -{% block title %}{{ block.super }} - {% trans "Verify Your E-mail Address" %}{% endblock title %} -{% block page_title %}{% trans "Verify Your E-mail Address" %}{% endblock page_title %} +{% block title %}{{ block.super }} - {% translate "Verify Your E-mail Address" %}{% endblock title %} +{% block page_title %}{% translate "Verify Your E-mail Address" %}{% endblock page_title %} {% block content %} -

{% blocktrans %}We have sent an e-mail to you for verification. Follow the link provided to finalize the signup process. Please contact us if you do not receive it within a few minutes.{% endblocktrans %}

+

{% blocktranslate trimmed %} + We have sent an e-mail to you for verification. Follow the link provided to finalize the signup process. Please + contact us if you do not receive it within a few minutes. + {% endblocktranslate %}

{% endblock %} diff --git a/gnuviechadmin/templates/account/verified_email_required.html b/gnuviechadmin/templates/account/verified_email_required.html index eb3c5e1..fa7ef4f 100644 --- a/gnuviechadmin/templates/account/verified_email_required.html +++ b/gnuviechadmin/templates/account/verified_email_required.html @@ -1,18 +1,19 @@ {% extends "account/base.html" %} {% load i18n %} -{% block title %}{{ block.super }} - {% trans "Verify Your E-mail Address" %}{% endblock title %} -{% block page_title %}{% trans "Verify Your E-mail Address" %}{% endblock %} +{% block title %}{{ block.super }} - {% translate "Verify Your E-mail Address" %}{% endblock title %} +{% block page_title %}{% translate "Verify Your E-mail Address" %}{% endblock %} {% block content %} -{% url 'account_email' as email_url %} -

{% blocktrans %}This part of the site requires us to verify that -you are who you claim to be. For this purpose, we require that you -verify ownership of your e-mail address. {% endblocktrans %}

+ {% url 'account_email' as email_url %} +

{% blocktranslate trimmed %}This part of the site requires us to verify that + you are who you claim to be. For this purpose, we require that you + verify ownership of your e-mail address.{% endblocktranslate %}

-

{% blocktrans %}We have sent an e-mail to you for -verification. Please click on the link inside this e-mail. Please -contact us if you do not receive it within a few minutes.{% endblocktrans %}

+

{% blocktranslate trimmed %}We have sent an e-mail to you for + verification. Please click on the link inside this e-mail. Please + contact us if you do not receive it within a few minutes.{% endblocktranslate %}

-

{% blocktrans %}Note: you can still change your e-mail address.{% endblocktrans %}

+

{% blocktranslate trimmed %}Note: you can still + change your e-mail address.{% endblocktranslate %}

{% endblock %} diff --git a/gnuviechadmin/templates/base.html b/gnuviechadmin/templates/base.html index b6c10fb..4521c43 100644 --- a/gnuviechadmin/templates/base.html +++ b/gnuviechadmin/templates/base.html @@ -1,26 +1,16 @@ -{% load staticfiles i18n account %} +{% load static i18n account %} - + {% block title %}gnuviechadmin{% endblock title %} - + - + - - - - @@ -28,118 +18,151 @@ {% block extra_css %}{% endblock extra_css %} - + - - - + -
+
-

{% block page_title %}Example Base Template{% endblock page_title %}

+

{% block page_title %}Example Base Template{% endblock page_title %}

- {% if messages %} - {% for message in messages %} - - {% endfor %} - {% endif %} + {% if messages %} + {% for message in messages %} + + {% endfor %} + {% endif %} - {% block content %} + {% block content %}

Use this document as a way to quick start any new project.

- {% endblock content %} + {% endblock content %} -
+ - - - - - + + - - + + - {% block extra_js %}{% endblock extra_js %} - +{% block extra_js %}{% endblock extra_js %} + diff --git a/gnuviechadmin/templates/contact_form/contact_form.html b/gnuviechadmin/templates/contact_form/contact_form.html deleted file mode 100644 index 9416c41..0000000 --- a/gnuviechadmin/templates/contact_form/contact_form.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "contact_form/base.html" %} -{% load i18n crispy_forms_tags %} - -{% block title %}{{ block.super }} - {% trans "Contact" %}{% endblock title %} -{% block page_title %}{% trans "Contact" %}{% endblock page_title %} - -{% block content %} -{% crispy form %} -{% endblock %} - -{% block extra_js %} - -{% endblock extra_js %} diff --git a/gnuviechadmin/templates/contact_form/contact_success.html b/gnuviechadmin/templates/contact_form/contact_success.html deleted file mode 100644 index 6539087..0000000 --- a/gnuviechadmin/templates/contact_form/contact_success.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "contact_form/base.html" %} -{% load i18n %} - -{% block title %}{{ block.super }} - {% trans "Contact" %}{% endblock title %} -{% block page_title %}{% trans "Contact" %}{% endblock page_title %} - -{% block content %} -

{% trans "Your message has been sent successfully." %}

-{% endblock %} diff --git a/gnuviechadmin/templates/dashboard/index.html b/gnuviechadmin/templates/dashboard/index.html deleted file mode 100644 index 6b435eb..0000000 --- a/gnuviechadmin/templates/dashboard/index.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "base.html" %} -{% load i18n %} -{% block title %}{{ block.super }} - {% trans "Welcome" %}{% endblock title %} -{% block page_title %}{% trans "Welcome to our customer self service" %}{% endblock page_title %} -{% block content %} -{% if user.is_authenticated %} -

{% url 'customer_dashboard' slug=user.username as dashboard_url %}{% blocktrans with full_name=user.get_full_name %}Hello {{ full_name }},
-You can visit your Dashboard to view and modify your hosting options. -{% endblocktrans %}

-{% else %} -

{% trans "This is your entry to our customer self service sytem." %}

-

{% url 'account_login' as login_url %}{% url 'dashboard' as dashboard_url %}{% blocktrans %}If you are already a customer you can Sign in to view and modify your hosting options.{% endblocktrans %}

-{% endif %} -{% endblock content %} diff --git a/gnuviechadmin/templates/dashboard/user_dashboard.html b/gnuviechadmin/templates/dashboard/user_dashboard.html deleted file mode 100644 index 7afb025..0000000 --- a/gnuviechadmin/templates/dashboard/user_dashboard.html +++ /dev/null @@ -1,51 +0,0 @@ -{% extends "base.html" %} -{% load i18n %} -{% block title %}{{ block.super }} - {% blocktrans with full_name=dashboard_user.get_full_name %}Dashboard for {{ full_name }}{% endblocktrans %}{% endblock title %} -{% block page_title %}{% blocktrans with full_name=dashboard_user.get_full_name %}Dashboard for {{ full_name }}{% endblocktrans %}{% endblock page_title %} -{% block content %} -
-
-
-
{% trans "Hosting packages" %}
-
- {% if hosting_packages %} - - - - - - - - - - - - {% for package in hosting_packages %} - - - - - - - - {% endfor %} - -
{% trans "Name" %}{% trans "Disk space" %}{% trans "Mailboxes" %}{% trans "Databases" %}{% trans "Actions" %}
{{ package.name }} - {% with diskspace=package.get_disk_space %} - {{ diskspace|filesizeformat }} - {% endwith %} - {% blocktrans with num=package.used_mailbox_count total=package.mailbox_count %}used {{ num }} of {{ total }}{% endblocktrans %}{% 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 %}
- {% else %} -

{% if user == object %}{% trans "You have no hosting packages yet." %}{% else %}{% trans "This user has no hosting packages assigned yet." %}{% endif %}

- {% endif %} - {% if user.is_staff %} - {% trans "Add hosting package" %} - {% endif %} -
-
-
-
-{% endblock content %} diff --git a/gnuviechadmin/templates/domains/hostingdomain_create.html b/gnuviechadmin/templates/domains/hostingdomain_create.html index d2ab6f8..54aed5f 100644 --- a/gnuviechadmin/templates/domains/hostingdomain_create.html +++ b/gnuviechadmin/templates/domains/hostingdomain_create.html @@ -1,16 +1,21 @@ {% extends "domains/base.html" %} {% load i18n crispy_forms_tags %} -{% block title %}{{ block.super }} - {% blocktrans with package=hostingpackage.name full_name=customer.get_full_name %}Add Domain to Hosting Package {{ package }} of Customer {{ full_name }}{% endblocktrans %}{% endblock title %} -{% block page_title %}{% blocktrans with package=hostingpackage.name full_name=customer.get_full_name %}Add Domain to Hosting Package {{ package }} of Customer {{ full_name }}{% endblocktrans %}{% endblock page_title %} +{% block title %}{{ block.super }} - + {% blocktranslate with package=hostingpackage.name full_name=customer.get_full_name trimmed %} + Add Domain 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 Domain to Hosting Package {{ package }} of Customer {{ full_name }} +{% endblocktranslate %}{% endblock page_title %} {% block content %} -{% crispy form %} + {% crispy form %} {% endblock content %} {% block extra_js %} - + {% endblock extra_js %} diff --git a/gnuviechadmin/templates/hostingpackages/add_hosting_option.html b/gnuviechadmin/templates/hostingpackages/add_hosting_option.html deleted file mode 100644 index 76b013e..0000000 --- a/gnuviechadmin/templates/hostingpackages/add_hosting_option.html +++ /dev/null @@ -1,8 +0,0 @@ -{% extends "hostingpackages/base.html" %} -{% load i18n crispy_forms_tags %} -{% block title %}{{ block.super }} - {% blocktrans with package=hostingpackage.name full_name=customer.get_full_name %}Add Option to Hosting Package {{ package }} of Customer {{ full_name }}{% endblocktrans %}{% endblock title %} -{% block page_title %}{% blocktrans with package=hostingpackage.name full_name=customer.get_full_name %}Add Option to Hosting Package {{ package }} of Customer {{ full_name }}{% endblocktrans %}{% endblock page_title %} - -{% block content %} -{% crispy form %} -{% endblock content %} diff --git a/gnuviechadmin/templates/hostingpackages/customerhostingpackage_admin_list.html b/gnuviechadmin/templates/hostingpackages/customerhostingpackage_admin_list.html deleted file mode 100644 index 73623b1..0000000 --- a/gnuviechadmin/templates/hostingpackages/customerhostingpackage_admin_list.html +++ /dev/null @@ -1,32 +0,0 @@ -{% extends "hostingpackages/base.html" %} -{% load i18n %} -{% block title %}{{ block.super }} - {% trans "All hosting packages" %}{% endblock title %} -{% block page_title %}{% trans "All hosting packages" %}{% endblock page_title %} - -{% block content %} -{% if customerhostingpackage_list %} - - - - - - - - - - {% for package in customerhostingpackage_list %} - - - - - - {% endfor %} - -
{% trans "Name" %}{% trans "Customer" %}{% trans "Setup date" %}
{{ package.name }}{{ package.customer }}{{ package.created }}
-{% else %} -

{% trans "There are no hosting packages setup yet." %}

-{% endif %} -

-{% trans "Add hosting package" %} -

-{% endblock content %} diff --git a/gnuviechadmin/templates/hostingpackages/customerhostingpackage_create.html b/gnuviechadmin/templates/hostingpackages/customerhostingpackage_create.html deleted file mode 100644 index 1cadd46..0000000 --- a/gnuviechadmin/templates/hostingpackages/customerhostingpackage_create.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "hostingpackages/base.html" %} -{% load i18n crispy_forms_tags %} -{% block title %}{{ block.super }} - {% blocktrans with full_name=customer.get_full_name %}Add hosting package for Customer {{ full_name }}{% endblocktrans %}{% endblock title %} -{% block page_title %}{% blocktrans with full_name=customer.get_full_name %}Add Hosting Package for Customer {{ full_name }}{% endblocktrans %}{% endblock page_title %} -{% block content %} -{% crispy form %} -{% endblock content %} diff --git a/gnuviechadmin/templates/hostingpackages/customerhostingpackage_detail.html b/gnuviechadmin/templates/hostingpackages/customerhostingpackage_detail.html deleted file mode 100644 index c135d59..0000000 --- a/gnuviechadmin/templates/hostingpackages/customerhostingpackage_detail.html +++ /dev/null @@ -1,213 +0,0 @@ -{% extends "hostingpackages/base.html" %} -{% load i18n %} - -{% block title %}{{ block.super }} - {% spaceless %} -{% if user == customer %} - {% blocktrans with package=hostingpackage.name %}Details for your Hosting Package {{ package }}{% endblocktrans %} -{% else %} - {% blocktrans with package=hostingpackage.name full_name=customer.get_full_name %}Details for Hosting Package {{ package }} of {{ full_name }}{% endblocktrans %} -{% endif %} -{% endspaceless %}{% endblock title %} - -{% block page_title %}{% blocktrans with package=hostingpackage.name %}Details of Hosting Package {{ package }}{% endblocktrans %}{% endblock page_title %} - -{% block content %} -
-
-
-
{% spaceless %} - {% trans "Hosting Package Information" %} - {% if user.is_staff %} -
- -
- {% endif %} - {% endspaceless %}
-
-
{% trans "Name" %}
-
{{ hostingpackage.name }}
-
{% trans "Description" %}
-
{{ hostingpackage.description|default:"-" }}
-
{% trans "Disk space" %}
- {% with diskspace=hostingpackage.get_disk_space packagespace=hostingpackage.get_package_space %} -
- {{ diskspace|filesizeformat }} - -
- {% endwith %} -
{% trans "Mailboxes" %}
-
{% blocktrans with num=hostingpackage.used_mailbox_count total=hostingpackage.mailbox_count %}{{ num }} of {{ total }} in use{% endblocktrans %}
-
{% if osuser.is_sftp_user %}{% trans "SFTP username" %}{% else %}{% trans "SSH/SFTP username" %}{% endif %}
-
{{ osuser.username }}{% if sshkeys %} {{ sshkeys|length }}{% endif %}
-
{% trans "Upload server" %}
-
{{ uploadserver }}
-
-
-
-
-
-
{% trans "Hosting Package Options" %}
- {% if hostingoptions %} -
    - {% for opt in hostingoptions %} -
  • {{ opt }}
  • - {% endfor %} -
- {% else %} -

{% trans "No options booked" %}

- {% endif %} - {% if user.is_staff %} -

{% trans "Add option" %}

- {% endif %} -
-
- -
-
-
-
-
{% trans "Domains" %}
- {% if domains %} - - - - - - - - - - - {% for domain in domains %} - - - {% if domain.domain.maildomain.mailaddress_set.exists %} - - {% else %} - - {% endif %} - {% if domain.domain.website_set.exists %} - - {% else %} - - {% endif %} - - - {% endfor %} - -
{% trans "Domain name" %}{% trans "Mail addresses" %}{% trans "Websites" %}{% trans "Actions" %}
{{ domain.domain }} - {% with maildomain=domain.domain.maildomain %} - {% for mailaddress in maildomain.mailaddresses %}{% spaceless %} - {{ mailaddress }} - {% trans "Delete mail address" %} - {% endspaceless %}{% if not forloop.last %}, {% endif %}{% endfor %} - {% endwith %} - {% trans "None" %} - {% with domain=domain.domain %} - {% for website in domain.website_set.all %}{% spaceless %} - {{ website }} - {% trans "Delete website" %} - {% endspaceless %}{% if not forloop.last %}, {% endif %}{% endfor %} - {% endwith %} - {% trans "None" %} - {% if domain.domain.maildomain %} - {% with maildomain=domain.domain.maildomain %} - {% trans "Add mail address" %} - {% endwith %} - {% endif %} - {% with hostingdomain=domain.domain %} - {% trans "Add website" %} - {% endwith %} -
- {% else %} -

{% trans "There are no domains assigned to this hosting package yet." %}

- {% endif %} - {% if user.is_staff %} -

{% trans "Add domain" %}

- {% endif %} -
-
-
-
-
-
-
{% trans "E-Mail-Accounts" %}
- {% if mailboxes %} - - - - - - - - - - - {% for mailbox in mailboxes %} - - - - - - {% endfor %} - -
{% trans "Mailbox" %}{% trans "Mail addresses" %}{% trans "Active" %}{% trans "Actions" %}
{{ mailbox.username }}{{ mailbox.mailaddresses|join:", " }} {% if mailbox.active %}{% trans "Active" %}{% else %}{% trans "inactive" %}{% endif %} - {% trans "Set mailbox password" %} -
- {% else %} -

{% trans "There are no mailboxes assigned to this hosting package yet." %}

- {% endif %} - {% if hostingpackage.may_add_mailbox %} -

{% trans "Add mailbox" %}

- {% endif %} -
-
-
-
-
-
-
{% trans "Databases" %}
- {% if databases %} - - - - - - - - - - - {% for database in databases %} - - - - - - - {% endfor %} - -
{% trans "Database name" %}{% trans "Database user" %}{% trans "Type" %}{% trans "Actions" %}
{{ database.db_name }}{{ database.db_user.name }}{% include "userdbs/snippets/db_type.html" with db_type=database.db_user.db_type %} - {% trans "Set database user password" %} - {% trans "Delete database" %} -
- {% else %} -

{% trans "There are no databases assigned to this hosting package yet." %}

- {% endif %} - {% if hostingpackage.may_add_database %} -

{% trans "Add database" %}

- {% endif %} -
-
-
-{% endblock content %} diff --git a/gnuviechadmin/templates/hostingpackages/customerhostingpackage_list.html b/gnuviechadmin/templates/hostingpackages/customerhostingpackage_list.html deleted file mode 100644 index fb90f1d..0000000 --- a/gnuviechadmin/templates/hostingpackages/customerhostingpackage_list.html +++ /dev/null @@ -1,43 +0,0 @@ -{% extends "hostingpackages/base.html" %} -{% load i18n %} -{% block title %}{{ block.super }} - {% spaceless %} -{% if user == customer %} -{% trans "Your hosting packages" %} -{% else %} -{% blocktrans with customer=customer.get_full_name %}Hosting Packages of {{ customer }}{% endblocktrans %} -{% endif %} -{% endspaceless %}{% endblock title %} - -{% block page_title %}{% spaceless %} -{% if user == customer %} -{% trans "Your hosting packages" %} -{% else %} -{% blocktrans with customer=customer.get_full_name %}Hosting Packages of {{ customer }}{% endblocktrans %} -{% endif %} -{% endspaceless %}{% endblock page_title %} - -{% block content %} -{% if customerhostingpackage_list %} - - - - - - - - - {% for package in customerhostingpackage_list %} - - - - - {% endfor %} - -
{% trans "Name" %}{% trans "Setup date" %}
{{ package.name }}{{ package.created }}
-{% else %} -

{% if user == customer %}{% trans "You have no hosting packages setup yet." %}{% else %}{% trans "There are no hosting packages setup for this customer yet." %}{% endif %}

-{% endif %} -{% if user.is_staff %} -

{% trans "Add hosting package" %}

-{% endif %} -{% endblock content %} diff --git a/gnuviechadmin/templates/hostingpackages/customerhostingpackage_option_choices.html b/gnuviechadmin/templates/hostingpackages/customerhostingpackage_option_choices.html deleted file mode 100644 index 51c71a4..0000000 --- a/gnuviechadmin/templates/hostingpackages/customerhostingpackage_option_choices.html +++ /dev/null @@ -1,23 +0,0 @@ -{% extends "hostingpackages/base.html"%} -{% load i18n %} - -{% block title %}{{ block.super }} - {% blocktrans with package=hostingpackage.name full_name=customer.get_full_name %}Choose new Option for Hosting Package {{ package }} of Customer {{ full_name }}{% endblocktrans %}{% endblock title %} - -{% block page_title %}{% blocktrans with package=hostingpackage.name full_name=customer.get_full_name %}Choose new Option for Hosting Package {{ package }} of Customer {{ full_name }}{% endblocktrans %}{% endblock page_title %} - -{% block content %} -
- {% for label, items in hosting_options %} -
-
-
{{ label }}
-
    - {% for item, option_type in items %} -
  • {{ item }}
  • - {% endfor %} -
-
-
- {% endfor %} -
-{% endblock %} diff --git a/gnuviechadmin/templates/impersonate/list_users.html b/gnuviechadmin/templates/impersonate/list_users.html new file mode 100644 index 0000000..f8ddd9b --- /dev/null +++ b/gnuviechadmin/templates/impersonate/list_users.html @@ -0,0 +1,33 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block title %}{{ block.super }} - {% translate "Django Impersonate - User List" %}{% endblock title %} +{% block page_title %}{% blocktranslate trimmed %} + User List - Page {{ page_number }} +{% endblocktranslate %}{% endblock page_title %} + +{% block content %} + {% if page.object_list %} + + {% endif %} + +

+ {% translate "Search users" %} +

+ +

+ {% if page.has_previous %} + Previous Page   + {% endif %} + + {% if page.has_next %} + Next Page   + {% endif %} +

+{% endblock %} \ No newline at end of file diff --git a/gnuviechadmin/templates/impersonate/search_users.html b/gnuviechadmin/templates/impersonate/search_users.html new file mode 100644 index 0000000..7de8c90 --- /dev/null +++ b/gnuviechadmin/templates/impersonate/search_users.html @@ -0,0 +1,42 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block title %}{{ block.super }} - {% translate "Django Impersonate - Search Users" %}{% endblock title %} +{% block page_title %}Search Users {% if query %}- Page {{ page_number }}{% endif %}{% endblock page_title %} + +{% block content %} +
+ {{ redirect_field }} +
+ + +
+ + {% translate "List all users" %} +
+ +

+ {% if query and page.object_list %} +

+ {% endif %} +

+ +

+ {% if query and page.has_previous %} + Previous + Page   + {% endif %} + + {% if query and page.has_next %} + Next + Page + {% endif %} +

+{% endblock %} \ No newline at end of file diff --git a/gnuviechadmin/templates/managemails/mailaddress_confirm_delete.html b/gnuviechadmin/templates/managemails/mailaddress_confirm_delete.html index d520ce1..6abcf8e 100644 --- a/gnuviechadmin/templates/managemails/mailaddress_confirm_delete.html +++ b/gnuviechadmin/templates/managemails/mailaddress_confirm_delete.html @@ -2,33 +2,37 @@ {% load i18n %} {% block title %}{{ block.super }} - {% spaceless %} -{% if user == customer %} -{% blocktrans %}Delete Mail Address {{ mailaddress }}{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %}Delete Mail Address {{ mailaddress }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate %}Delete Mail Address {{ mailaddress }}{% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + Delete Mail Address {{ mailaddress }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock title %} {% block page_title %}{% spaceless %} -{% if user == customer %} -{% blocktrans %}Delete Mail Address {{ mailaddress }}{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %}Delete Mail Address {{ mailaddress }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate %}Delete Mail Address {{ mailaddress }}{% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + Delete Mail Address {{ mailaddress }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock page_title %} {% block content %} -
-
- {% blocktrans %}Do you really want to delete the mail address {{ mailaddress }}?{% endblocktrans %} -
-
-
- {% csrf_token %} - - {% trans "Cancel" %} -
-
-
-{% endblock content %} - +
+
{% blocktranslate trimmed %} + Do you really want to delete the mail address {{ mailaddress }}? + {% endblocktranslate %}
+
+
+ {% csrf_token %} + + {% translate "Cancel" %} +
+
+
+{% endblock content %} \ No newline at end of file diff --git a/gnuviechadmin/templates/managemails/mailaddress_create.html b/gnuviechadmin/templates/managemails/mailaddress_create.html index 5ae5b62..cd5a6f7 100644 --- a/gnuviechadmin/templates/managemails/mailaddress_create.html +++ b/gnuviechadmin/templates/managemails/mailaddress_create.html @@ -2,33 +2,35 @@ {% load i18n crispy_forms_tags %} {% block title %}{{ block.super }} - {% spaceless %} -{% if user == customer %} -{% blocktrans %}Add new Mail Address{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %} -Add new Mail Address for Customer {{ full_name }} -{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate %}Add new Mail Address{% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + Add new Mail Address for Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock title %} {% block page_title %}{% spaceless %} -{% if user == customer %} -{% blocktrans %}Add new Mail Address{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %} -Add new Mail Address for Customer {{ full_name }} -{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate %}Add new Mail Address{% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + Add new Mail Address for Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock page_title %} {% block content %} -{% crispy form %} + {% crispy form %} {% endblock content %} {% block extra_js %} - -{% endblock extra_js %} + +{% endblock extra_js %} \ No newline at end of file diff --git a/gnuviechadmin/templates/managemails/mailaddress_edit.html b/gnuviechadmin/templates/managemails/mailaddress_edit.html index 8102a5e..e53224f 100644 --- a/gnuviechadmin/templates/managemails/mailaddress_edit.html +++ b/gnuviechadmin/templates/managemails/mailaddress_edit.html @@ -2,25 +2,25 @@ {% load i18n crispy_forms_tags %} {% block title %}{{ block.super }} - {% spaceless %} -{% if user == customer %} -{% blocktrans %}Change target of Mail Address{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %} -Change target of Mail Address for Customer {{ full_name }} -{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate %}Change target of Mail Address{% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + Change target of Mail Address for Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock title %} {% block page_title %}{% spaceless %} -{% if user == customer %} -{% blocktrans %}Change target of Mail Address{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %} -Change target of Mail Address for Customer {{ full_name }} -{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate %}Change target of Mail Address{% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + Change target of Mail Address for Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock page_title %} {% block content %} -{% crispy form %} + {% crispy form %} {% endblock content %} diff --git a/gnuviechadmin/templates/managemails/mailbox_create.html b/gnuviechadmin/templates/managemails/mailbox_create.html index 7446d6c..716d1e1 100644 --- a/gnuviechadmin/templates/managemails/mailbox_create.html +++ b/gnuviechadmin/templates/managemails/mailbox_create.html @@ -1,34 +1,47 @@ {% extends "managemails/base.html" %} {% load i18n crispy_forms_tags %} {% block title %}{{ block.user }} - {% spaceless %} -{% with full_name=customer.get_full_name package=hostingpackage.name %} -{% if customer == user %} -{% blocktrans %}Add Mailbox to Hosting Package {{ package }}{% endblocktrans %} -{% else %} -{% blocktrans %}Add Mailbox to Hosting Package {{ package }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} -{% endwith %} + {% with full_name=customer.get_full_name package=hostingpackage.name %} + {% if customer == user %} + {% blocktranslate %}Add Mailbox to Hosting Package {{ package }}{% endblocktranslate %} + {% else %} + {% blocktranslate trimmed %} + Add Mailbox to Hosting Package {{ package }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} + {% endwith %} {% endspaceless %}{% endblock title %} {% block page_title %}{% spaceless %} -{% with full_name=customer.get_full_name package=hostingpackage.name %} -{% if customer == user %} -{% blocktrans %}Add Mailbox to Hosting Package {{ package }}{% endblocktrans %} -{% else %} -{% blocktrans %}Add Mailbox to Hosting Package {{ package }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} -{% endwith %} + {% with full_name=customer.get_full_name package=hostingpackage.name %} + {% if customer == user %} + {% blocktranslate %}Add Mailbox to Hosting Package {{ package }}{% endblocktranslate %} + {% else %} + {% blocktranslate trimmed %} + Add Mailbox to Hosting Package {{ package }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} + {% endwith %} {% endspaceless %}{% endblock page_title %} {% block content %} -

{% if customer == user %}{% trans "Please specify the password for your new mailbox." %}{% else %}{% trans "Please specify the password for the new mailbox." %}{% endif %}

-{% crispy form %} +

{% if customer == user %} + {% translate "Please specify the password for your new mailbox." %} + {% else %} + {% translate "Please specify the password for the new mailbox." %} + {% endif %}

+ {% crispy form %} {% endblock content %} {% block extra_js %} - -{% endblock extra_js %} + +{% endblock extra_js %} \ No newline at end of file diff --git a/gnuviechadmin/templates/managemails/mailbox_setpassword.html b/gnuviechadmin/templates/managemails/mailbox_setpassword.html index 10829b5..5de1256 100644 --- a/gnuviechadmin/templates/managemails/mailbox_setpassword.html +++ b/gnuviechadmin/templates/managemails/mailbox_setpassword.html @@ -1,34 +1,44 @@ {% extends "managemails/base.html" %} {% load i18n crispy_forms_tags %} {% block title %}{{ block.user }} - {% spaceless %} -{% with full_name=customer.get_full_name mailbox=mailbox.username %} -{% if customer == user %} -{% blocktrans %}Set Password for Mailbox {{ mailbox }}{% endblocktrans %} -{% else %} -{% blocktrans %}Set Password for Mailbox {{ mailbox }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} -{% endwith %} + {% with full_name=customer.get_full_name mailbox=mailbox.username %} + {% if customer == user %} + {% blocktranslate %}Set Password for Mailbox {{ mailbox }}{% endblocktranslate %} + {% else %} + {% blocktranslate trimmed %} + Set Password for Mailbox {{ mailbox }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} + {% endwith %} {% endspaceless %}{% endblock title %} {% block page_title %}{% spaceless %} -{% with full_name=customer.get_full_name mailbox=mailbox.username %} -{% if customer == user %} -{% blocktrans %}Set Password for Mailbox {{ mailbox }}{% endblocktrans %} -{% else %} -{% blocktrans %}Set Password for Mailbox {{ mailbox }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} -{% endwith %} + {% with full_name=customer.get_full_name mailbox=mailbox.username %} + {% if customer == user %} + {% blocktranslate %}Set Password for Mailbox {{ mailbox }}{% endblocktranslate %} + {% else %} + {% blocktranslate trimmed %} + Set Password for Mailbox {{ mailbox }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} + {% endwith %} {% endspaceless %}{% endblock page_title %} {% block content %} -

{% if customer == user %}{% trans "Please specify the new password for your mailbox." %}{% else %}{% trans "Please specify the new password for the mailbox." %}{% endif %}

-{% crispy form %} +

{% if customer == user %}{% translate "Please specify the new password for your mailbox." %}{% else %} + {% translate "Please specify the new password for the mailbox." %}{% endif %}

+ {% crispy form %} {% endblock content %} {% block extra_js %} - -{% endblock extra_js %} + +{% endblock extra_js %} \ No newline at end of file diff --git a/gnuviechadmin/templates/osusers/sshpublickey_confirm_delete.html b/gnuviechadmin/templates/osusers/sshpublickey_confirm_delete.html index 022346b..868b2fa 100644 --- a/gnuviechadmin/templates/osusers/sshpublickey_confirm_delete.html +++ b/gnuviechadmin/templates/osusers/sshpublickey_confirm_delete.html @@ -2,34 +2,46 @@ {% load i18n crispy_forms_tags %} {% block title %}{{ block.super }} - {% spaceless %} -{% if user == customer %} -{% blocktrans %}Delete SSH Public Key for Operating System User {{ osuser }}{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %}Delete SSH Public Key for Operating System User {{ osuser }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate %}Delete SSH Public Key for Operating System User {{ osuser }}{% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + Delete SSH Public Key for Operating System User {{ osuser }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock title %} {% block page_title %}{% spaceless %} -{% if user == customer %} -{% blocktrans %}Delete SSH Public Key for Operating System User {{ osuser }}{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %}Delete SSH Public Key for Operating System User {{ osuser }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate trimmed %} + Delete SSH Public Key for Operating System User {{ osuser }} + {% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + Delete SSH Public Key for Operating System User {{ osuser }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock page_title %} {% block content %} -
-
- {% blocktrans with algorithm=key.algorithm %}Do you really want to delete the {{ algorithm }} SSH public key?{% endblocktrans %} -
-
-

{% blocktrans %}When you confirm the deletion of this key you will no longer be able to use the corresponding private key for authentication.{% endblocktrans %}

-
{{ key }}
-
- {% csrf_token %} - - {% trans "Cancel" %} -
-
-
-{% endblock content %} +
+
+ {% blocktranslate with algorithm=key.algorithm trimmed %} + Do you really want to delete the {{ algorithm }} SSH public key? + {% endblocktranslate %} +
+
+

{% blocktranslate trimmed %} + When you confirm the deletion of this key you will no longer be able to use the corresponding private + key for authentication. + {% endblocktranslate %}

+
{{ key }}
+
+ {% csrf_token %} + + {% translate "Cancel" %} +
+
+
+{% endblock content %} \ No newline at end of file diff --git a/gnuviechadmin/templates/osusers/sshpublickey_create.html b/gnuviechadmin/templates/osusers/sshpublickey_create.html index bfda9df..a7fbdd8 100644 --- a/gnuviechadmin/templates/osusers/sshpublickey_create.html +++ b/gnuviechadmin/templates/osusers/sshpublickey_create.html @@ -2,29 +2,37 @@ {% load i18n crispy_forms_tags %} {% block title %}{{ block.super }} - {% spaceless %} -{% if user == customer %} -{% blocktrans %}Add new SSH Public Key for Operating System User {{ osuser }}{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %}Add a new SSH Public Key for Operating System User {{ osuser }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate trimmed %} + Add new SSH Public Key for Operating System User {{ osuser }} + {% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + Add a new SSH Public Key for Operating System User {{ osuser }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock title %} {% block page_title %}{% spaceless %} -{% if user == customer %} -{% blocktrans %}Add new SSH Public Key for Operating System User {{ osuser }}{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %}Add a new SSH Public Key for Operating System User {{ osuser }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate trimmed %} + Add new SSH Public Key for Operating System User {{ osuser }} + {% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + Add a new SSH Public Key for Operating System User {{ osuser }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock page_title %} {% block content %} -{% crispy form %} + {% crispy form %} {% endblock content %} {% block extra_js %} - -{% endblock extra_js %} + +{% endblock extra_js %} \ No newline at end of file diff --git a/gnuviechadmin/templates/osusers/sshpublickey_edit_comment.html b/gnuviechadmin/templates/osusers/sshpublickey_edit_comment.html index dcdac74..dbb9ecd 100644 --- a/gnuviechadmin/templates/osusers/sshpublickey_edit_comment.html +++ b/gnuviechadmin/templates/osusers/sshpublickey_edit_comment.html @@ -2,29 +2,36 @@ {% load i18n crispy_forms_tags %} {% block title %}{{ block.super }} - {% spaceless %} -{% if user == customer %} -{% blocktrans %}Edit Comment of SSH Public Key for Operating System User {{ osuser }}{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %}Edit Comment of SSH Public Key for Operating System User {{ osuser }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate %}Edit Comment of SSH Public Key for Operating System User {{ osuser }}{% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + Edit Comment of SSH Public Key for Operating System User {{ osuser }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock title %} {% block page_title %}{% spaceless %} -{% if user == customer %} -{% blocktrans %}Edit Comment of Public Key for Operating System User {{ osuser }}{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %}Edit Comment of SSH Public Key for Operating System User {{ osuser }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate trimmed %} + Edit Comment of Public Key for Operating System User {{ osuser }} + {% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + Edit Comment of SSH Public Key + for Operating System User {{ osuser }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock page_title %} {% block content %} -{% crispy form %} + {% crispy form %} {% endblock content %} {% block extra_js %} - -{% endblock extra_js %} + +{% endblock extra_js %} \ No newline at end of file diff --git a/gnuviechadmin/templates/osusers/sshpublickey_list.html b/gnuviechadmin/templates/osusers/sshpublickey_list.html index e94d47b..3ebe9d8 100644 --- a/gnuviechadmin/templates/osusers/sshpublickey_list.html +++ b/gnuviechadmin/templates/osusers/sshpublickey_list.html @@ -2,46 +2,60 @@ {% load i18n crispy_forms_tags %} {% block title %}{{ block.super }} - {% spaceless %} -{% if user == customer %} -{% blocktrans %}SSH Public Keys for Operating System User {{ osuser }}{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %}SSH Public Keys for Operating System User {{ osuser }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate %}SSH Public Keys for Operating System User {{ osuser }}{% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + SSH Public Keys for Operating System User {{ osuser }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock title %} {% block page_title %}{% spaceless %} -{% if user == customer %} -{% blocktrans %}SSH Public Keys for Operating System User {{ osuser }}{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %}SSH Public Keys for Operating System User {{ osuser }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate trimmed %} + SSH Public Keys for Operating System User {{ osuser }} + {% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + SSH Public Keys for Operating System User {{ osuser }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock page_title %} {% block content %} -{% if keys %} - - - - - - - - - - {% for key in keys %} - - - - - - {% endfor %} - -
{% trans "Algorithm" %}{% trans "Comment" %}{% trans "Actions" %}
{{ key.algorithm }}{{ key.comment }} - {% trans "Delete" %} - {% trans "Edit Comment" %} -
-{% else %} -

{% trans "There are now SSH public keys set for this operating system user yet." %}

-{% endif %} -

{% trans "Add SSH public key" %}

-{% endblock content %} + {% if keys %} + + + + + + + + + + {% for key in keys %} + + + + + + {% endfor %} + +
{% translate "Algorithm" %}{% translate "Comment" %}{% translate "Actions" %}
{{ key.algorithm }}{{ key.comment }} + {% translate "Delete" %} + {% translate "Edit Comment" %} +
+ {% else %} +

{% translate "There are now SSH public keys set for this operating system user yet." %}

+ {% endif %} +

{% translate "Add SSH public key" %}

+{% endblock content %} \ No newline at end of file diff --git a/gnuviechadmin/templates/osusers/user_setpassword.html b/gnuviechadmin/templates/osusers/user_setpassword.html index 86fa844..2f8cd6c 100644 --- a/gnuviechadmin/templates/osusers/user_setpassword.html +++ b/gnuviechadmin/templates/osusers/user_setpassword.html @@ -1,30 +1,39 @@ {% extends "osusers/base.html" %} {% load i18n crispy_forms_tags %} {% block title %}{{ block.super }} - {% spaceless %} -{% if customer == user %} - {% blocktrans with osuser=osuser.username %}Set new password for user {{ osuser }}{% endblocktrans %} -{% else %} - {% blocktrans with osuser=osuser.username full_name=customer.get_full_name %}Set new password for user {{ osuser }} of customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if customer == user %} + {% blocktranslate with osuser=osuser.username %}Set new password for user {{ osuser }}{% endblocktranslate %} + {% else %} + {% blocktranslate with osuser=osuser.username full_name=customer.get_full_name trimmed %} + Set new password for user {{ osuser }} of customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock title %} {% block page_title %}{% spaceless %} -{% if customer == user %} - {% blocktrans with osuser=osuser.username %}Set new password for user {{ osuser }}{% endblocktrans %} -{% else %} - {% blocktrans with osuser=osuser.username full_name=customer.get_full_name %}Set new password for user {{ osuser }} of customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if customer == user %} + {% blocktranslate with osuser=osuser.username %}Set new password for user {{ osuser }}{% endblocktranslate %} + {% else %} + {% blocktranslate with osuser=osuser.username full_name=customer.get_full_name trimmed %} + Set new password for user {{ osuser }} of customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock page_title %} {% block content %} -{% crispy form %} + {% crispy form %} {% endblock content %} {% block extra_js %} - -{% endblock extra_js %} + +{% endblock extra_js %} \ No newline at end of file diff --git a/gnuviechadmin/templates/registration/login.html b/gnuviechadmin/templates/registration/login.html index 6f21196..c923497 100644 --- a/gnuviechadmin/templates/registration/login.html +++ b/gnuviechadmin/templates/registration/login.html @@ -1,12 +1,12 @@ {% extends "base.html" %} {% load i18n crispy_forms_tags %} -{% block title %}{{ block.super }} - {% trans "Sign in" %}{% endblock title %} -{% block page_title %}{% trans "Sign In" %}{% endblock %} +{% block title %}{{ block.super }} - {% translate "Sign in" %}{% endblock title %} +{% block page_title %}{% translate "Sign In" %}{% endblock %} {% block content %} -
-{% csrf_token %} - -{{ form|crispy }} - -
-{% endblock %} +
+ {% csrf_token %} + + {{ form|crispy }} + +
+{% endblock %} \ No newline at end of file diff --git a/gnuviechadmin/templates/socialaccount/authentication_error.html b/gnuviechadmin/templates/socialaccount/authentication_error.html index e258760..4107e36 100644 --- a/gnuviechadmin/templates/socialaccount/authentication_error.html +++ b/gnuviechadmin/templates/socialaccount/authentication_error.html @@ -1,9 +1,9 @@ {% extends "socialaccount/base.html" %} {% load i18n %} -{% block title %}{{ block.super }} - {% trans "Social Network Login Failure" %}{% endblock title %} -{% block page_title %}{% trans "Social Network Login Failure" %}{% endblock page_title %} +{% block title %}{{ block.super }} - {% translate "Social Network Login Failure" %}{% endblock title %} +{% block page_title %}{% translate "Social Network Login Failure" %}{% endblock page_title %} {% block content %} -

{% trans "An error occurred while attempting to login via your social network account." %}

-{% endblock %} +

{% translate "An error occurred while attempting to login via your social network account." %}

+{% endblock %} \ No newline at end of file diff --git a/gnuviechadmin/templates/socialaccount/connections.html b/gnuviechadmin/templates/socialaccount/connections.html index 5b17e43..2b4dc59 100644 --- a/gnuviechadmin/templates/socialaccount/connections.html +++ b/gnuviechadmin/templates/socialaccount/connections.html @@ -1,50 +1,56 @@ {% extends "socialaccount/base.html" %} {% load i18n %} -{% block title %}{{ block.super }} - {% trans "Account Connections" %}{% endblock title %} -{% block page_title %}{% trans "Account Connections" %}{% endblock page_title %} +{% block title %}{{ block.super }} - {% translate "Account Connections" %}{% endblock title %} +{% block page_title %}{% translate "Account Connections" %}{% endblock page_title %} {% block content %} -{% if form.accounts %} -

{% blocktrans %}You can sign in to your account using any of the following third party accounts:{% endblocktrans %}

+ {% if form.accounts %} +

{% blocktranslate trimmed %} + You can sign in to your account using any of the following third party accounts: + {% endblocktranslate %}

-
- {% csrf_token %} - {% if form.non_field_errors %}
{{form.non_field_errors}}
{% endif %} + + {% csrf_token %} + {% if form.non_field_errors %} +
{{ form.non_field_errors }}
{% endif %} - - - - - - - - - - {% for base_account in form.accounts %} - {% with account=base_account.get_provider_account provider=base_account.provider %} - - - - - - {% endwith %} - {% endfor %} - -
{% trans "Select" %}{% trans "Provider" %}{% trans "Account name" %}
{{ account }}
- -
-{% else %} -

{% trans 'You currently have no social network accounts connected to this account.' %}

-{% endif %} + + + + + + + + + + {% for base_account in form.accounts %} + {% with account=base_account.get_provider_account provider=base_account.provider %} + + + + + + {% endwith %} + {% endfor %} + +
{% translate "Select" %}{% translate "Provider" %}{% translate "Account name" %}
+ {{ account }}
+ + + {% else %} +

{% translate 'You currently have no social network accounts connected to this account.' %}

+ {% endif %} -

{% trans 'Add a 3rd Party Account' %}

-
    - {% include "socialaccount/snippets/provider_list.html" with process="connect" %} -
+

{% translate 'Add a 3rd Party Account' %}

+
    + {% include "socialaccount/snippets/provider_list.html" with process="connect" %} +
{% endblock %} {% block extra_js %} -{% include "socialaccount/snippets/login_extra.html" %} -{% endblock extra_js %} + {% include "socialaccount/snippets/login_extra.html" %} +{% endblock extra_js %} \ No newline at end of file diff --git a/gnuviechadmin/templates/socialaccount/login.html b/gnuviechadmin/templates/socialaccount/login.html new file mode 100644 index 0000000..4fe7708 --- /dev/null +++ b/gnuviechadmin/templates/socialaccount/login.html @@ -0,0 +1,22 @@ +{% extends "socialaccount/base.html" %} +{% load i18n %} + +{% block title %}{% translate "Sign In" %}{% endblock %} +{% block page_title %}{% translate "Social Network Sign In" %}{% endblock page_title %} + +{% block content %} +{% if process == "connect" %} +

{% blocktranslate with provider.name as provider %}Connect {{ provider }}{% endblocktranslate %}

+ +

{% blocktranslate with provider.name as provider %}You are about to connect a new third party account from {{ provider }}.{% endblocktranslate %}

+{% else %} +

{% blocktranslate with provider.name as provider %}Sign In Via {{ provider }}{% endblocktranslate %}

+ +

{% blocktranslate with provider.name as provider %}You are about to sign in using a third party account from {{ provider }}.{% endblocktranslate %}

+{% endif %} + +
+ {% csrf_token %} + +
+{% endblock %} diff --git a/gnuviechadmin/templates/socialaccount/login_cancelled.html b/gnuviechadmin/templates/socialaccount/login_cancelled.html index f76deb4..453e3be 100644 --- a/gnuviechadmin/templates/socialaccount/login_cancelled.html +++ b/gnuviechadmin/templates/socialaccount/login_cancelled.html @@ -1,10 +1,13 @@ {% extends "socialaccount/base.html" %} {% load i18n %} -{% block title %}{{ block.super }} - {% trans "Login Cancelled" %}{% endblock title %} -{% block page_title %}{% trans "Login Cancelled" %}{% endblock page_title %} +{% block title %}{{ block.super }} - {% translate "Login Cancelled" %}{% endblock title %} +{% block page_title %}{% translate "Login Cancelled" %}{% endblock page_title %} {% block content %} -{% url 'account_login' as login_url %} -

{% blocktrans %}You decided to cancel logging in to our site using one of your existing accounts. If this was a mistake, please proceed to sign in.{% endblocktrans %}

-{% endblock %} + {% url 'account_login' as login_url %} +

{% blocktranslate trimmed %} + You decided to cancel logging in to our site using one of your existing accounts. If this was a mistake, please + proceed to sign in. + {% endblocktranslate %}

+{% endblock %} \ No newline at end of file diff --git a/gnuviechadmin/templates/socialaccount/messages/account_connected.txt b/gnuviechadmin/templates/socialaccount/messages/account_connected.txt index be6aa60..5922da1 100644 --- a/gnuviechadmin/templates/socialaccount/messages/account_connected.txt +++ b/gnuviechadmin/templates/socialaccount/messages/account_connected.txt @@ -1,2 +1,2 @@ {% load i18n %} -{% blocktrans %}The social account has been connected.{% endblocktrans %} +{% blocktranslate %}The social account has been connected.{% endblocktranslate %} diff --git a/gnuviechadmin/templates/socialaccount/messages/account_connected_other.txt b/gnuviechadmin/templates/socialaccount/messages/account_connected_other.txt index e90f6cc..fa7b905 100644 --- a/gnuviechadmin/templates/socialaccount/messages/account_connected_other.txt +++ b/gnuviechadmin/templates/socialaccount/messages/account_connected_other.txt @@ -1,2 +1,2 @@ {% load i18n %} -{% blocktrans %}The social account is already connected to a different account.{% endblocktrans %} +{% blocktranslate %}The social account is already connected to a different account.{% endblocktranslate %} diff --git a/gnuviechadmin/templates/socialaccount/messages/account_disconnected.txt b/gnuviechadmin/templates/socialaccount/messages/account_disconnected.txt index fd43f30..de79d35 100644 --- a/gnuviechadmin/templates/socialaccount/messages/account_disconnected.txt +++ b/gnuviechadmin/templates/socialaccount/messages/account_disconnected.txt @@ -1,2 +1,2 @@ {% load i18n %} -{% blocktrans %}The social account has been disconnected.{% endblocktrans %} +{% blocktranslate %}The social account has been disconnected.{% endblocktranslate %} \ No newline at end of file diff --git a/gnuviechadmin/templates/socialaccount/signup.html b/gnuviechadmin/templates/socialaccount/signup.html index b4f84f5..b3f22e3 100644 --- a/gnuviechadmin/templates/socialaccount/signup.html +++ b/gnuviechadmin/templates/socialaccount/signup.html @@ -1,17 +1,20 @@ {% extends "socialaccount/base.html" %} {% load i18n crispy_forms_tags %} -{% block title %}{{ block.super }} - {% trans "Signup" %}{% endblock title %} -{% block page_title %}{% trans "Sign Up" %}{% endblock page_title %} +{% block title %}{{ block.super }} - {% translate "Signup" %}{% endblock title %} +{% block page_title %}{% translate "Sign Up" %}{% endblock page_title %} {% block content %} -

{% blocktrans with provider_name=account.get_provider.name site_name=site.name %}You are about to use your {{provider_name}} account to login to -{{site_name}}. As a final step, please complete the following form:{% endblocktrans %}

+

{% blocktranslate with provider_name=account.get_provider.name site_name=site.name trimmed %} + You are about to use your {{ provider_name }} account to login to {{ site_name }}. As a final step, please + complete the following form: + {% endblocktranslate %}

- + {% endblock %} diff --git a/gnuviechadmin/templates/socialaccount/snippets/provider_list.html b/gnuviechadmin/templates/socialaccount/snippets/provider_list.html index 3b7d9fb..8cd97f9 100644 --- a/gnuviechadmin/templates/socialaccount/snippets/provider_list.html +++ b/gnuviechadmin/templates/socialaccount/snippets/provider_list.html @@ -1,22 +1,24 @@ {% load socialaccount %} {% get_providers as socialaccount_providers %} -{% for provider in socialaccount_providers %} -{% if provider.id == "openid" %} -{% for brand in provider.get_brands %} -
  • - {{brand.name}} -
  • -{% endfor %} -{% endif %} -
  • -  {{provider.name}} -
  • -{% endfor %} - +
      + {% for provider in socialaccount_providers %} + {% if provider.id == "openid" %} + {% for brand in provider.get_brands %} +
    • + {{ brand.name }} +
    • + {% endfor %} + {% endif %} +
    • +  {{ provider.name }} + +
    • + {% endfor %} +
    diff --git a/gnuviechadmin/templates/userdbs/databaseuser_setpassword.html b/gnuviechadmin/templates/userdbs/databaseuser_setpassword.html index 37d58dc..00ed733 100644 --- a/gnuviechadmin/templates/userdbs/databaseuser_setpassword.html +++ b/gnuviechadmin/templates/userdbs/databaseuser_setpassword.html @@ -2,30 +2,46 @@ {% load i18n crispy_forms_tags %} {% block title %}{{ block.super }} - {% spaceless %} -{% if customer == user %} -{% blocktrans with dbuser=dbuser.name %}Set Database User Password for {{ dbuser }}{% endblocktrans %} -{% else %} -{% blocktrans with dbuser=dbuser.name full_name=customer.get_full_name %}Set Database User Password for {{ dbuser }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if customer == user %} + {% blocktranslate with dbuser=dbuser.name %}Set Database User Password for {{ dbuser }}{% endblocktranslate %} + {% else %} + {% blocktranslate with dbuser=dbuser.name full_name=customer.get_full_name trimmed %} + Set Database User Password for {{ dbuser }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock title %} {% block page_title %}{% spaceless %} -{% if customer == user %} -{% blocktrans with dbuser=dbuser.name %}Set Database User Password for {{ dbuser }}{% endblocktrans %} -{% else %} -{% blocktrans with dbuser=dbuser.name full_name=customer.get_full_name %}Set Database User Password for {{ dbuser }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if customer == user %} + {% blocktranslate with dbuser=dbuser.name trimmed %} + Set Database User Password for {{ dbuser }} + {% endblocktranslate %} + {% else %} + {% blocktranslate with dbuser=dbuser.name full_name=customer.get_full_name trimmed %} + Set Database User Password for {{ dbuser }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock page_title %} {% block content %} -

    {% if customer == user %}{% trans "Please specify the new password for your database user." %}{% else %}{% trans "Please specify the new password of the database user." %}{% endif %} -{% crispy form %} +

    {% spaceless %}{% if customer == user %} + {% translate "Please specify the new password for your database user." %} + {% else %} + {% translate "Please specify the new password of the database user." %} + {% endif %}{% endspaceless %}

    + {% crispy form %} {% endblock content %} {% block extra_js %} - -{% endblock extra_js %} + +{% endblock extra_js %} \ No newline at end of file diff --git a/gnuviechadmin/templates/userdbs/snippets/db_type.html b/gnuviechadmin/templates/userdbs/snippets/db_type.html index e6df7a5..2421963 100644 --- a/gnuviechadmin/templates/userdbs/snippets/db_type.html +++ b/gnuviechadmin/templates/userdbs/snippets/db_type.html @@ -1,3 +1,3 @@ {# format database types #} {% load userdb %} -{% db_type_name %} +{% db_type_name %} diff --git a/gnuviechadmin/templates/userdbs/userdatabase_confirm_delete.html b/gnuviechadmin/templates/userdbs/userdatabase_confirm_delete.html index 4c71342..3482211 100644 --- a/gnuviechadmin/templates/userdbs/userdatabase_confirm_delete.html +++ b/gnuviechadmin/templates/userdbs/userdatabase_confirm_delete.html @@ -2,33 +2,46 @@ {% load i18n %} {% block title %}{{ block.super }} - {% spaceless %} -{% if user == customer %} -{% blocktrans with database=database.db_name %}Delete Database {{ database }}{% endblocktrans %} -{% else %} -{% blocktrans with database=database.db_name full_name=customer.get_full_name %}Delete Database {{ database }} of customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate with database=database.db_name %}Delete Database {{ database }}{% endblocktranslate %} + {% else %} + {% blocktranslate with database=database.db_name full_name=customer.get_full_name trimmed %} + Delete Database {{ database }} of customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock title %} {% block page_title %}{% spaceless %} -{% if user == customer %} -{% blocktrans with database=database.db_name %}Delete Database {{ database }}{% endblocktrans %} -{% else %} -{% blocktrans with database=database.db_name full_name=customer.get_full_name %}Delete Database {{ database }} of customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate with database=database.db_name trimmed %} + Delete Database {{ database }} + {% endblocktranslate %} + {% else %} + {% blocktranslate with database=database.db_name full_name=customer.get_full_name trimmed %} + Delete Database {{ database }} of customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock page_title %} {% block content %} -
    -
    - {% blocktrans with database=database.db_name %}Do you really want to delete the database {{ database }}?{% endblocktrans %} -
    -
    -

    {% blocktrans %}When you confirm the deletion the database will be removed from the database server. All data in the database will be lost! If the database user assigned to that database has no other databases assigned it will be deleted too.{% endblocktrans %}

    -
    - {% csrf_token %} - - {% trans "Cancel" %} -
    -
    -
    -{% endblock content %} +
    +
    + {% blocktranslate with database=database.db_name trimmed %} + Do you really want to delete the database {{ database }}? + {% endblocktranslate %} +
    +
    +

    {% blocktranslate trimmed %} + When you confirm the deletion the database will be removed from the database server. + All data in the database will be lost! If the database user assigned to that database + has no other databases assigned it will be deleted too. + {% endblocktranslate %}

    +
    + {% csrf_token %} + + {% translate "Cancel" %} +
    +
    +
    +{% endblock content %} \ No newline at end of file diff --git a/gnuviechadmin/templates/userdbs/userdatabase_create.html b/gnuviechadmin/templates/userdbs/userdatabase_create.html index 48356a4..9360ff6 100644 --- a/gnuviechadmin/templates/userdbs/userdatabase_create.html +++ b/gnuviechadmin/templates/userdbs/userdatabase_create.html @@ -2,30 +2,40 @@ {% load i18n crispy_forms_tags %} {% block title %}{{ block.super }} - {% spaceless %} -{% if user == customer %} -{% blocktrans %}Add new Database{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %}Add new Database for Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate %}Add new Database{% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + Add new Database for Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock title %} {% block page_title %}{% spaceless %} -{% if user == customer %} -{% blocktrans %}Add new Database{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %}Add new Database for Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate %}Add new Database{% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + Add new Database for Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock page_title %} {% block content %} -

    {% blocktrans %}Please enter a password for a new database user for your database.{% endblocktrans %}

    -{% crispy form %} +

    {% blocktranslate %}Please enter a password for a new database user for your database.{% endblocktranslate %}

    + {% crispy form %} {% endblock content %} {% block extra_js %} - -{% endblock %} + +{% endblock %} \ No newline at end of file diff --git a/gnuviechadmin/templates/websites/website_confirm_delete.html b/gnuviechadmin/templates/websites/website_confirm_delete.html index 92cb0e9..c7dcf8d 100644 --- a/gnuviechadmin/templates/websites/website_confirm_delete.html +++ b/gnuviechadmin/templates/websites/website_confirm_delete.html @@ -2,33 +2,42 @@ {% load i18n %} {% block title %}{{ block.super }} - {% spaceless %} -{% if user == customer %} -{% blocktrans %}Delete Website {{ website }}{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %}Delete Website {{ website }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate %}Delete Website {{ website }}{% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + Delete Website {{ website }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock title %} {% block page_title %}{% spaceless %} -{% if user == customer %} -{% blocktrans %}Delete Website {{ website }}{% endblocktrans %} -{% else %} -{% blocktrans with full_name=customer.get_full_name %}Delete Website {{ website }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate %}Delete Website {{ website }}{% endblocktranslate %} + {% else %} + {% blocktranslate with full_name=customer.get_full_name trimmed %} + Delete Website {{ website }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock page_title %} {% block content %} -
    -
    - {% blocktrans %}Do you really want to delete the website {{ website }}?{% endblocktrans %} -
    -
    -

    {% blocktrans %}Please be aware that the website directory is removed from the webserver and the webserver configuration is changed so that the website will not be reachable anymore. All data in the website directory will be lost!{% endblocktrans %}

    -
    - {% csrf_token %} - - {% trans "Cancel" %} -
    -
    -
    -{% endblock content %} +
    +
    + {% blocktranslate %}Do you really want to delete the website {{ website }}?{% endblocktranslate %} +
    +
    +

    {% blocktranslate trimmed %} + Please be aware that the website directory is removed from the webserver and the webserver configuration + is changed so that the website will not be reachable anymore. + All data in the website directory will be lost! + {% endblocktranslate %}

    +
    + {% csrf_token %} + + {% translate "Cancel" %} +
    +
    +
    +{% endblock content %} \ No newline at end of file diff --git a/gnuviechadmin/templates/websites/website_create.html b/gnuviechadmin/templates/websites/website_create.html index 3fb890f..c2e1ffc 100644 --- a/gnuviechadmin/templates/websites/website_create.html +++ b/gnuviechadmin/templates/websites/website_create.html @@ -2,29 +2,35 @@ {% load i18n crispy_forms_tags %} {% block title %}{{ block.super }} - {% spaceless %} -{% if user == customer %} -{% blocktrans with domain=domain.domain %}Add Website for Subdomain of {{ domain }}{% endblocktrans %} -{% else %} -{% blocktrans with domain=domain.domain full_name=customer.get_full_name %}Add Website for Subdomain of Domain {{ domain }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate with domain=domain.domain %}Add Website for Subdomain of {{ domain }}{% endblocktranslate %} + {% else %} + {% blocktranslate with domain=domain.domain full_name=customer.get_full_name trimmed %} + Add Website for Subdomain of Domain {{ domain }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock title %} {% block page_title %}{% spaceless %} -{% if user == customer %} -{% blocktrans with domain=domain.domain %}Add Website for Subdomain of {{ domain }}{% endblocktrans %} -{% else %} -{% blocktrans with domain=domain.domain full_name=customer.get_full_name %}Add Website for Subdomain of Domain {{ domain }} of Customer {{ full_name }}{% endblocktrans %} -{% endif %} + {% if user == customer %} + {% blocktranslate with domain=domain.domain trimmed %} + Add Website for Subdomain of {{ domain }} + {% endblocktranslate %} + {% else %} + {% blocktranslate with domain=domain.domain full_name=customer.get_full_name trimmed %} + Add Website for Subdomain of Domain {{ domain }} of Customer {{ full_name }} + {% endblocktranslate %} + {% endif %} {% endspaceless %}{% endblock page_title %} {% block content %} -{% crispy form %} + {% crispy form %} {% endblock %} {% block extra_js %} - -{% endblock extra_js %} + +{% endblock extra_js %} \ No newline at end of file diff --git a/gnuviechadmin/userdbs/__init__.py b/gnuviechadmin/userdbs/__init__.py index 6f1b8ad..0e6da36 100644 --- a/gnuviechadmin/userdbs/__init__.py +++ b/gnuviechadmin/userdbs/__init__.py @@ -2,4 +2,3 @@ This app is for managing database users and user databases. """ -default_app_config = 'userdbs.apps.UserdbsAppConfig' diff --git a/gnuviechadmin/userdbs/admin.py b/gnuviechadmin/userdbs/admin.py index 9b807e2..fd08960 100644 --- a/gnuviechadmin/userdbs/admin.py +++ b/gnuviechadmin/userdbs/admin.py @@ -6,12 +6,9 @@ from __future__ import absolute_import from django import forms from django.contrib import admin -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ -from .models import ( - DatabaseUser, - UserDatabase, -) +from .models import DatabaseUser, UserDatabase class DatabaseUserCreationForm(forms.ModelForm): @@ -23,7 +20,7 @@ class DatabaseUserCreationForm(forms.ModelForm): class Meta: model = DatabaseUser - fields = ['osuser', 'db_type'] + fields = ["osuser", "db_type"] def save(self, commit=True): """ @@ -35,8 +32,10 @@ class DatabaseUserCreationForm(forms.ModelForm): """ dbuser = DatabaseUser.objects.create_database_user( - osuser=self.cleaned_data['osuser'], - db_type=self.cleaned_data['db_type'], commit=commit) + osuser=self.cleaned_data["osuser"], + db_type=self.cleaned_data["db_type"], + commit=commit, + ) return dbuser def save_m2m(self): @@ -55,7 +54,7 @@ class UserDatabaseCreationForm(forms.ModelForm): class Meta: model = UserDatabase - fields = ['db_user'] + fields = ["db_user"] def save(self, commit=True): """ @@ -67,7 +66,8 @@ class UserDatabaseCreationForm(forms.ModelForm): """ database = UserDatabase.objects.create_userdatabase( - db_user=self.cleaned_data['db_user'], commit=commit) + db_user=self.cleaned_data["db_user"], commit=commit + ) return database def save_m2m(self): @@ -83,7 +83,8 @@ class DatabaseUserAdmin(admin.ModelAdmin): ` """ - actions = ['perform_delete_selected'] + + actions = ["perform_delete_selected"] add_form = DatabaseUserCreationForm def get_form(self, request, obj=None, **kwargs): @@ -101,12 +102,13 @@ class DatabaseUserAdmin(admin.ModelAdmin): """ defaults = {} if obj is None: - defaults.update({ - 'form': self.add_form, - }) + defaults.update( + { + "form": self.add_form, + } + ) defaults.update(kwargs) - return super(DatabaseUserAdmin, self).get_form( - request, obj, **defaults) + return super(DatabaseUserAdmin, self).get_form(request, obj, **defaults) def get_readonly_fields(self, request, obj=None): """ @@ -122,7 +124,7 @@ class DatabaseUserAdmin(admin.ModelAdmin): """ if obj: - return ['osuser', 'name', 'db_type'] + return ["osuser", "name", "db_type"] return [] def save_model(self, request, obj, form, change): @@ -154,8 +156,8 @@ class DatabaseUserAdmin(admin.ModelAdmin): """ for dbuser in queryset.all(): dbuser.delete() - perform_delete_selected.short_description = _( - 'Delete selected database users') + + perform_delete_selected.short_description = _("Delete selected database users") def get_actions(self, request): """ @@ -170,8 +172,8 @@ class DatabaseUserAdmin(admin.ModelAdmin): """ actions = super(DatabaseUserAdmin, self).get_actions(request) - if 'delete_selected' in actions: # pragma: no cover - del actions['delete_selected'] + if "delete_selected" in actions: # pragma: no cover + del actions["delete_selected"] return actions @@ -181,7 +183,8 @@ class UserDatabaseAdmin(admin.ModelAdmin): ` """ - actions = ['perform_delete_selected'] + + actions = ["perform_delete_selected"] add_form = UserDatabaseCreationForm def get_form(self, request, obj=None, **kwargs): @@ -199,12 +202,13 @@ class UserDatabaseAdmin(admin.ModelAdmin): """ defaults = {} if obj is None: - defaults.update({ - 'form': self.add_form, - }) + defaults.update( + { + "form": self.add_form, + } + ) defaults.update(kwargs) - return super(UserDatabaseAdmin, self).get_form( - request, obj, **defaults) + return super(UserDatabaseAdmin, self).get_form(request, obj, **defaults) def get_readonly_fields(self, request, obj=None): """ @@ -220,7 +224,7 @@ class UserDatabaseAdmin(admin.ModelAdmin): """ if obj: - return ['db_name', 'db_user'] + return ["db_name", "db_user"] return [] def save_model(self, request, obj, form, change): @@ -252,8 +256,8 @@ class UserDatabaseAdmin(admin.ModelAdmin): """ for database in queryset.all(): database.delete() - perform_delete_selected.short_description = _( - 'Delete selected user databases') + + perform_delete_selected.short_description = _("Delete selected user databases") def get_actions(self, request): """ @@ -268,8 +272,8 @@ class UserDatabaseAdmin(admin.ModelAdmin): """ actions = super(UserDatabaseAdmin, self).get_actions(request) - if 'delete_selected' in actions: # pragma: no cover - del actions['delete_selected'] + if "delete_selected" in actions: # pragma: no cover + del actions["delete_selected"] return actions diff --git a/gnuviechadmin/userdbs/apps.py b/gnuviechadmin/userdbs/apps.py index 304f4d2..b48ab66 100644 --- a/gnuviechadmin/userdbs/apps.py +++ b/gnuviechadmin/userdbs/apps.py @@ -3,10 +3,8 @@ This module contains the :py:class:`django.apps.AppConfig` instance for the :py:mod:`userdbs` app. """ -from __future__ import unicode_literals - from django.apps import AppConfig -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ class UserdbsAppConfig(AppConfig): @@ -14,8 +12,9 @@ class UserdbsAppConfig(AppConfig): AppConfig for the :py:mod:`userdbs` app. """ - name = 'userdbs' - verbose_name = _('Database Users and their Databases') + + name = "userdbs" + verbose_name = _("Database Users and their Databases") def ready(self): """ diff --git a/gnuviechadmin/userdbs/forms.py b/gnuviechadmin/userdbs/forms.py index bf9a099..5c2d983 100644 --- a/gnuviechadmin/userdbs/forms.py +++ b/gnuviechadmin/userdbs/forms.py @@ -2,32 +2,27 @@ This module defines form classes for user database editing. """ -from __future__ import absolute_import, unicode_literals - -from django import forms -from django.urls import reverse -from django.utils.translation import ugettext_lazy as _ +from __future__ import absolute_import from crispy_forms.helper import FormHelper -from crispy_forms.layout import ( - Submit, -) +from crispy_forms.layout import Submit +from django import forms +from django.urls import reverse +from django.utils.translation import gettext_lazy as _ -from .models import ( - DB_TYPES, - DatabaseUser, - UserDatabase, -) from gvawebcore.forms import PasswordModelFormMixin +from .models import DB_TYPES, DatabaseUser, UserDatabase + class AddUserDatabaseForm(forms.ModelForm, PasswordModelFormMixin): """ This form is used to create new user database instances. """ + db_type = forms.TypedChoiceField( - label=_('Database type'), + label=_("Database type"), choices=DB_TYPES, widget=forms.RadioSelect, coerce=int, @@ -38,17 +33,18 @@ class AddUserDatabaseForm(forms.ModelForm, PasswordModelFormMixin): fields = [] def __init__(self, *args, **kwargs): - self.hosting_package = kwargs.pop('hostingpackage') - self.available_dbtypes = kwargs.pop('dbtypes') + self.hosting_package = kwargs.pop("hostingpackage") + self.available_dbtypes = kwargs.pop("dbtypes") super(AddUserDatabaseForm, self).__init__(*args, **kwargs) - self.fields['db_type'].choices = self.available_dbtypes + self.fields["db_type"].choices = self.available_dbtypes if len(self.available_dbtypes) == 1: - self.fields['db_type'].initial = self.available_dbtypes[0][0] - self.fields['db_type'].widget = forms.HiddenInput() + self.fields["db_type"].initial = self.available_dbtypes[0][0] + self.fields["db_type"].widget = forms.HiddenInput() self.helper = FormHelper() self.helper.form_action = reverse( - 'add_userdatabase', kwargs={'package': self.hosting_package.id}) - self.helper.add_input(Submit('submit', _('Create database'))) + "add_userdatabase", kwargs={"package": self.hosting_package.id} + ) + self.helper.add_input(Submit("submit", _("Create database"))) def save(self, commit=True): """ @@ -62,8 +58,11 @@ class AddUserDatabaseForm(forms.ModelForm, PasswordModelFormMixin): """ data = self.cleaned_data self.instance = UserDatabase.objects.create_userdatabase_with_user( - data['db_type'], self.hosting_package.osuser, - password=data['password1'], commit=commit) + data["db_type"], + self.hosting_package.osuser, + password=data["password1"], + commit=commit, + ) return super(AddUserDatabaseForm, self).save(commit) @@ -72,21 +71,24 @@ class ChangeDatabaseUserPasswordForm(forms.ModelForm, PasswordModelFormMixin): This form is used to change the password of a database user. """ + class Meta: model = DatabaseUser fields = [] def __init__(self, *args, **kwargs): - self.hosting_package = kwargs.pop('hostingpackage') + self.hosting_package = kwargs.pop("hostingpackage") super(ChangeDatabaseUserPasswordForm, self).__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_action = reverse( - 'change_dbuser_password', kwargs={ - 'slug': self.instance.name, - 'package': self.hosting_package.id, - }) - self.helper.add_input(Submit('submit', _('Set password'))) + "change_dbuser_password", + kwargs={ + "slug": self.instance.name, + "package": self.hosting_package.id, + }, + ) + self.helper.add_input(Submit("submit", _("Set password"))) def save(self, commit=True): - self.instance.set_password(self.cleaned_data['password1']) + self.instance.set_password(self.cleaned_data["password1"]) return super(ChangeDatabaseUserPasswordForm, self).save() diff --git a/gnuviechadmin/userdbs/locale/de/LC_MESSAGES/django.po b/gnuviechadmin/userdbs/locale/de/LC_MESSAGES/django.po index ca534d3..6dda0b7 100644 --- a/gnuviechadmin/userdbs/locale/de/LC_MESSAGES/django.po +++ b/gnuviechadmin/userdbs/locale/de/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: gnuviechadmin userdbs\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-01-29 11:04+0100\n" -"PO-Revision-Date: 2016-01-29 11:06+0100\n" +"POT-Creation-Date: 2023-04-16 22:07+0200\n" +"PO-Revision-Date: 2023-04-16 18:21+0200\n" "Last-Translator: Jan Dittberner \n" "Language-Team: Jan Dittberner \n" "Language: de\n" @@ -16,83 +16,84 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.8.6\n" +"X-Generator: Poedit 3.2.2\n" "X-Poedit-SourceCharset: UTF-8\n" -#: userdbs/admin.py:158 +#: userdbs/admin.py:160 msgid "Delete selected database users" msgstr "Ausgewählte Datenbanknutzer löschen" -#: userdbs/admin.py:256 +#: userdbs/admin.py:260 msgid "Delete selected user databases" msgstr "Ausgewählte Nutzerdatenbanken löschen" -#: userdbs/apps.py:18 +#: userdbs/apps.py:17 msgid "Database Users and their Databases" msgstr "Datenbanknutzer und ihre Datenbanken" -#: userdbs/forms.py:30 +#: userdbs/forms.py:25 msgid "Database type" msgstr "Datenbanktyp" -#: userdbs/forms.py:51 +#: userdbs/forms.py:47 msgid "Create database" msgstr "Datenbank anlegen" -#: userdbs/forms.py:88 +#: userdbs/forms.py:90 msgid "Set password" msgstr "Passwort setzen" -#: userdbs/models.py:13 +#: userdbs/models.py:10 msgid "PostgreSQL" msgstr "PostgreSQL" -#: userdbs/models.py:14 +#: userdbs/models.py:11 msgid "MySQL" msgstr "MySQL" -#: userdbs/models.py:88 +#: userdbs/models.py:82 msgid "username" msgstr "Benutzername" -#: userdbs/models.py:90 +#: userdbs/models.py:83 msgid "database type" msgstr "Datenbanktyp" -#: userdbs/models.py:96 userdbs/models.py:206 +#: userdbs/models.py:89 userdbs/models.py:197 msgid "database user" msgstr "Datenbanknutzer" -#: userdbs/models.py:97 +#: userdbs/models.py:90 msgid "database users" msgstr "Datenbanknutzer" -#: userdbs/models.py:205 +#: userdbs/models.py:195 msgid "database name" msgstr "Datenbankname" -#: userdbs/models.py:212 +#: userdbs/models.py:204 msgid "user database" msgstr "Nutzerdatenbank" -#: userdbs/models.py:213 +#: userdbs/models.py:205 msgid "user specific database" msgstr "nutzerspezifische Datenbank" -#: userdbs/views.py:54 +#: userdbs/views.py:44 msgid "The hosting package has no database products assigned." msgstr "Dem Hostingpaket sind keine Datenbankprodukte zugewiesen." -#: userdbs/views.py:67 +#: userdbs/views.py:59 #, python-brace-format msgid "Successfully create new {type} database {dbname} for user {dbuser}." -msgstr "Neue {type}-Datenbank {dbname} für Benutzer {dbuser} erfolgreich angelegt." +msgstr "" +"Neue {type}-Datenbank {dbname} für Benutzer {dbuser} erfolgreich angelegt." -#: userdbs/views.py:104 +#: userdbs/views.py:98 #, python-brace-format msgid "Successfully changed password of database user {dbuser}." msgstr "Passwort des Datenbanknutzers {dbuser} wurde erfolgreich geändert." -#: userdbs/views.py:133 +#: userdbs/views.py:131 msgid "Database deleted." msgstr "Datenbank gelöscht." diff --git a/gnuviechadmin/userdbs/migrations/0001_initial.py b/gnuviechadmin/userdbs/migrations/0001_initial.py index 54015a3..6a3402e 100644 --- a/gnuviechadmin/userdbs/migrations/0001_initial.py +++ b/gnuviechadmin/userdbs/migrations/0001_initial.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - import django.utils.timezone import model_utils.fields from django.db import migrations, models @@ -8,66 +6,110 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('osusers', '0004_auto_20150104_1751'), + ("osusers", "0004_auto_20150104_1751"), ] operations = [ migrations.CreateModel( - name='DatabaseUser', + name="DatabaseUser", 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( - max_length=63, verbose_name='username')), - ('db_type', models.PositiveSmallIntegerField( - verbose_name='database type', - choices=[(0, 'PostgreSQL'), (1, 'MySQL')])), - ('osuser', models.ForeignKey( - to='osusers.User', 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(max_length=63, verbose_name="username")), + ( + "db_type", + models.PositiveSmallIntegerField( + verbose_name="database type", + choices=[(0, "PostgreSQL"), (1, "MySQL")], + ), + ), + ( + "osuser", + models.ForeignKey(to="osusers.User", on_delete=models.CASCADE), + ), ], options={ - 'verbose_name': 'database user', - 'verbose_name_plural': 'database users', + "verbose_name": "database user", + "verbose_name_plural": "database users", }, bases=(models.Model,), ), migrations.CreateModel( - name='UserDatabase', + name="UserDatabase", 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)), - ('db_name', models.CharField( - max_length=63, verbose_name='database name')), - ('db_user', models.ForeignKey( - verbose_name='database user', to='userdbs.DatabaseUser', - 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, + ), + ), + ( + "db_name", + models.CharField(max_length=63, verbose_name="database name"), + ), + ( + "db_user", + models.ForeignKey( + verbose_name="database user", + to="userdbs.DatabaseUser", + on_delete=models.CASCADE, + ), + ), ], options={ - 'verbose_name': 'user database', - 'verbose_name_plural': 'user specific database', + "verbose_name": "user database", + "verbose_name_plural": "user specific database", }, bases=(models.Model,), ), migrations.AlterUniqueTogether( - name='userdatabase', - unique_together={('db_name', 'db_user')}, + name="userdatabase", + unique_together={("db_name", "db_user")}, ), migrations.AlterUniqueTogether( - name='databaseuser', - unique_together={('name', 'db_type')}, + name="databaseuser", + unique_together={("name", "db_type")}, ), ] diff --git a/gnuviechadmin/userdbs/models.py b/gnuviechadmin/userdbs/models.py index c9eafb5..879cc3c 100644 --- a/gnuviechadmin/userdbs/models.py +++ b/gnuviechadmin/userdbs/models.py @@ -1,24 +1,21 @@ -from __future__ import unicode_literals - from django.db import models, transaction from django.dispatch import Signal -from django.utils.encoding import python_2_unicode_compatible -from django.utils.translation import ugettext as _ +from django.utils.translation import gettext as _ from model_utils import Choices from model_utils.models import TimeStampedModel from osusers.models import User as OsUser DB_TYPES = Choices( - (0, 'pgsql', _('PostgreSQL')), - (1, 'mysql', _('MySQL')), + (0, "pgsql", _("PostgreSQL")), + (1, "mysql", _("MySQL")), ) """ Database type choice enumeration. """ -password_set = Signal(providing_args=['instance', 'password']) +password_set = Signal() class DatabaseUserManager(models.Manager): @@ -40,10 +37,10 @@ class DatabaseUserManager(models.Manager): dbuser_name_format = "{0}db{{0:02d}}".format(osuser.username) nextname = dbuser_name_format.format(count) - for user in self.values('name').filter( - osuser=osuser, db_type=db_type - ).order_by('name'): - if user['name'] == nextname: + for user in ( + self.values("name").filter(osuser=osuser, db_type=db_type).order_by("name") + ): + if user["name"] == nextname: count += 1 nextname = dbuser_name_format.format(count) else: @@ -74,33 +71,29 @@ class DatabaseUserManager(models.Manager): """ if username is None: username = self._get_next_dbuser_name(osuser, db_type) - db_user = DatabaseUser( - osuser=osuser, db_type=db_type, name=username) + db_user = DatabaseUser(osuser=osuser, db_type=db_type, name=username) if commit: db_user.save() return db_user -@python_2_unicode_compatible class DatabaseUser(TimeStampedModel, models.Model): osuser = models.ForeignKey(OsUser, on_delete=models.CASCADE) - name = models.CharField( - _('username'), max_length=63) - db_type = models.PositiveSmallIntegerField( - _('database type'), choices=DB_TYPES) + name = models.CharField(_("username"), max_length=63) + db_type = models.PositiveSmallIntegerField(_("database type"), choices=DB_TYPES) objects = DatabaseUserManager() class Meta: - unique_together = ['name', 'db_type'] - verbose_name = _('database user') - verbose_name_plural = _('database users') + unique_together = ["name", "db_type"] + verbose_name = _("database user") + verbose_name_plural = _("database users") def __str__(self): return "%(name)s (%(db_type)s for %(osuser)s)" % { - 'name': self.name, - 'db_type': self.get_db_type_display(), - 'osuser': self.osuser.username, + "name": self.name, + "db_type": self.get_db_type_display(), + "osuser": self.osuser.username, } @transaction.atomic @@ -110,8 +103,7 @@ class DatabaseUser(TimeStampedModel, models.Model): :param str password: new password for the database user """ - password_set.send( - sender=self.__class__, password=password, instance=self) + password_set.send(sender=self.__class__, password=password, instance=self) @transaction.atomic def delete(self, *args, **kwargs): @@ -149,10 +141,8 @@ class UserDatabaseManager(models.Manager): db_name_format = "{0}_{{0:02d}}".format(db_user.name) # first db is named the same as the user nextname = db_user.name - for name in self.values('db_name').filter(db_user=db_user).order_by( - 'db_name' - ): - if name['db_name'] == nextname: + for name in self.values("db_name").filter(db_user=db_user).order_by("db_name"): + if name["db_name"] == nextname: count += 1 nextname = db_name_format.format(count) else: @@ -161,7 +151,8 @@ class UserDatabaseManager(models.Manager): @transaction.atomic def create_userdatabase_with_user( - self, db_type, osuser, password=None, commit=True): + self, db_type, osuser, password=None, commit=True + ): """ Creates a new user database with a new user. @@ -175,7 +166,8 @@ class UserDatabaseManager(models.Manager): """ dbuser = DatabaseUser.objects.create_database_user( - osuser, db_type, password=password, commit=commit) + osuser, db_type, password=password, commit=commit + ) database = self.create_userdatabase(dbuser, commit=commit) return database @@ -198,24 +190,22 @@ class UserDatabaseManager(models.Manager): return database -@python_2_unicode_compatible class UserDatabase(TimeStampedModel, models.Model): # MySQL limits to 64, PostgreSQL to 63 characters - db_name = models.CharField( - _('database name'), max_length=63) + db_name = models.CharField(_("database name"), max_length=63) db_user = models.ForeignKey( - DatabaseUser, verbose_name=_('database user'), - on_delete=models.CASCADE) + DatabaseUser, verbose_name=_("database user"), on_delete=models.CASCADE + ) objects = UserDatabaseManager() class Meta: - unique_together = ['db_name', 'db_user'] - verbose_name = _('user database') - verbose_name_plural = _('user specific database') + unique_together = ["db_name", "db_user"] + verbose_name = _("user database") + verbose_name_plural = _("user specific database") def __str__(self): return "%(db_name)s (%(db_user)s)" % { - 'db_name': self.db_name, - 'db_user': self.db_user, + "db_name": self.db_name, + "db_user": self.db_user, } diff --git a/gnuviechadmin/userdbs/signals.py b/gnuviechadmin/userdbs/signals.py index 4a5bc32..d9c70b0 100644 --- a/gnuviechadmin/userdbs/signals.py +++ b/gnuviechadmin/userdbs/signals.py @@ -6,20 +6,26 @@ The module starts Celery_ tasks. .. _Celery: http://www.celeryproject.org/ """ -from __future__ import unicode_literals - import logging from django.db.models.signals import post_delete, post_save from django.dispatch import receiver -from passlib.utils import generate_password +from passlib.pwd import genword -from mysqltasks.tasks import (create_mysql_database, create_mysql_user, - delete_mysql_database, delete_mysql_user, - set_mysql_userpassword) -from pgsqltasks.tasks import (create_pgsql_database, create_pgsql_user, - delete_pgsql_database, delete_pgsql_user, - set_pgsql_userpassword) +from mysqltasks.tasks import ( + create_mysql_database, + create_mysql_user, + delete_mysql_database, + delete_mysql_user, + set_mysql_userpassword, +) +from pgsqltasks.tasks import ( + create_pgsql_database, + create_pgsql_user, + delete_pgsql_database, + delete_pgsql_user, + set_pgsql_userpassword, +) from taskresults.models import TaskResult from .models import DB_TYPES, DatabaseUser, UserDatabase, password_set @@ -64,25 +70,29 @@ def handle_dbuser_password_set(sender, instance, password, **kwargs): """ if instance.db_type == DB_TYPES.mysql: taskresult = TaskResult.objects.create_task_result( - 'handle_dbuser_password_set', + "handle_dbuser_password_set", set_mysql_userpassword.s(instance.name, password), - 'mysql password change') + "mysql password change", + ) _LOGGER.info( - 'MySQL password change has been requested in task %s', - taskresult.task_id) + "MySQL password change has been requested in task %s", taskresult.task_id + ) elif instance.db_type == DB_TYPES.pgsql: taskresult = TaskResult.objects.create_task_result( - 'handle_dbuser_password_set', + "handle_dbuser_password_set", set_pgsql_userpassword.s(instance.name, password), - 'pgsql password change') + "pgsql password change", + ) _LOGGER.info( - 'PostgreSQL password change has been requested in task %s', - taskresult.task_id) + "PostgreSQL password change has been requested in task %s", + taskresult.task_id, + ) else: _LOGGER.warning( - 'Password change has been requested for unknown database %s' - ' the request has been ignored.', - instance.db_type) + "Password change has been requested for unknown database %s" + " the request has been ignored.", + instance.db_type, + ) @receiver(post_save, sender=DatabaseUser) @@ -122,32 +132,37 @@ def handle_dbuser_created(sender, instance, created, **kwargs): """ if created: - password = kwargs.get('password', generate_password()) + password = kwargs.get("password", genword()) # TODO: send GPG encrypted mail with this information if instance.db_type == DB_TYPES.mysql: taskresult = TaskResult.objects.create_task_result( - 'handle_dbuser_created', + "handle_dbuser_created", create_mysql_user.s(instance.name, password), - 'mysql user creation') + "mysql user creation", + ) _LOGGER.info( - 'A new MySQL user %s creation has been requested in task %s', - instance.name, taskresult.task_id) + "A new MySQL user %s creation has been requested in task %s", + instance.name, + taskresult.task_id, + ) elif instance.db_type == DB_TYPES.pgsql: taskresult = TaskResult.objects.create_task_result( - 'handle_dbuser_created', + "handle_dbuser_created", create_pgsql_user.s(instance.name, password), - 'pgsql user creation') + "pgsql user creation", + ) _LOGGER.info( - 'A new PostgreSQL user %s creation has been requested in task' - ' %s', - instance.name, taskresult.task_id) + "A new PostgreSQL user %s creation has been requested in task" " %s", + instance.name, + taskresult.task_id, + ) else: _LOGGER.warning( - 'created DatabaseUser for unknown database type %s', - instance.db_type) + "created DatabaseUser for unknown database type %s", instance.db_type + ) _LOGGER.debug( - 'database user %s has been %s', - instance, created and "created" or "updated") + "database user %s has been %s", instance, created and "created" or "updated" + ) @receiver(post_delete, sender=DatabaseUser) @@ -185,26 +200,33 @@ def handle_dbuser_deleted(sender, instance, **kwargs): """ if instance.db_type == DB_TYPES.mysql: taskresult = TaskResult.objects.create_task_result( - 'handle_dbuser_deleted', + "handle_dbuser_deleted", delete_mysql_user.s(instance.name), - 'mysql user deletion') + "mysql user deletion", + ) _LOGGER.info( - 'MySQL user %s deletion has been requested in task %s', - instance.name, taskresult.task_id) + "MySQL user %s deletion has been requested in task %s", + instance.name, + taskresult.task_id, + ) elif instance.db_type == DB_TYPES.pgsql: taskresult = TaskResult.objects.create_task_result( - 'handle_dbuser_deleted', + "handle_dbuser_deleted", delete_pgsql_user.s(instance.name), - 'pgsql user deletion') + "pgsql user deletion", + ) _LOGGER.info( - 'PostgreSQL user %s deletion has been requested in task %s', - instance.name, taskresult.task_id) + "PostgreSQL user %s deletion has been requested in task %s", + instance.name, + taskresult.task_id, + ) else: _LOGGER.warning( - 'deleted DatabaseUser %s for unknown database type %s', - instance.name, instance.db_type) - _LOGGER.debug( - 'database user %s has been deleted', instance) + "deleted DatabaseUser %s for unknown database type %s", + instance.name, + instance.db_type, + ) + _LOGGER.debug("database user %s has been deleted", instance) @receiver(post_save, sender=UserDatabase) @@ -245,31 +267,36 @@ def handle_userdb_created(sender, instance, created, **kwargs): if created: if instance.db_user.db_type == DB_TYPES.mysql: taskresult = TaskResult.objects.create_task_result( - 'handle_userdb_created', - create_mysql_database.s( - instance.db_name, instance.db_user.name), - 'mysql database creation') + "handle_userdb_created", + create_mysql_database.s(instance.db_name, instance.db_user.name), + "mysql database creation", + ) _LOGGER.info( - 'The creation of a new MySQL database %s has been requested in' - ' task %s', - instance.db_name, taskresult.task_id) + "The creation of a new MySQL database %s has been requested in" + " task %s", + instance.db_name, + taskresult.task_id, + ) elif instance.db_user.db_type == DB_TYPES.pgsql: taskresult = TaskResult.objects.create_task_result( - 'handle_userdb_created', - create_pgsql_database.s( - instance.db_name, instance.db_user.name), - 'pgsql database creation') + "handle_userdb_created", + create_pgsql_database.s(instance.db_name, instance.db_user.name), + "pgsql database creation", + ) _LOGGER.info( - 'The creation of a new PostgreSQL database %s has been' - ' requested in task %s', - instance.db_name, taskresult.task_id) + "The creation of a new PostgreSQL database %s has been" + " requested in task %s", + instance.db_name, + taskresult.task_id, + ) else: _LOGGER.warning( - 'created UserDatabase for unknown database type %s', - instance.db_user.db_type) + "created UserDatabase for unknown database type %s", + instance.db_user.db_type, + ) _LOGGER.debug( - 'database %s has been %s', - instance, created and "created" or "updated") + "database %s has been %s", instance, created and "created" or "updated" + ) @receiver(post_delete, sender=UserDatabase) @@ -307,25 +334,31 @@ def handle_userdb_deleted(sender, instance, **kwargs): """ if instance.db_user.db_type == DB_TYPES.mysql: taskresult = TaskResult.objects.create_task_result( - 'handle_userdb_deleted', + "handle_userdb_deleted", delete_mysql_database.s(instance.db_name, instance.db_user.name), - 'mysql database deletion') + "mysql database deletion", + ) _LOGGER.info( - 'The deletion of MySQL database %s has been requested in task %s', - instance.db_name, taskresult.task_id) + "The deletion of MySQL database %s has been requested in task %s", + instance.db_name, + taskresult.task_id, + ) elif instance.db_user.db_type == DB_TYPES.pgsql: taskresult = TaskResult.objects.create_task_result( - 'handle_userdb_deleted', + "handle_userdb_deleted", delete_pgsql_database.s(instance.db_name), - 'pgsql database deletion') + "pgsql database deletion", + ) _LOGGER.info( - 'The deletion of PostgreSQL database %s has been requested in ' - ' task %s', - instance.db_name, taskresult.task_id) + "The deletion of PostgreSQL database %s has been requested in " " task %s", + instance.db_name, + taskresult.task_id, + ) else: _LOGGER.warning( - 'deleted UserDatabase %s of unknown type %s', - instance.db_name, instance.db_type) + "deleted UserDatabase %s of unknown type %s", + instance.db_name, + instance.db_type, + ) pass - _LOGGER.debug( - 'database %s has been deleted', instance) + _LOGGER.debug("database %s has been deleted", instance) diff --git a/gnuviechadmin/userdbs/tests/templatetags/test_userdb.py b/gnuviechadmin/userdbs/tests/templatetags/test_userdb.py index 120f18c..ad0a161 100644 --- a/gnuviechadmin/userdbs/tests/templatetags/test_userdb.py +++ b/gnuviechadmin/userdbs/tests/templatetags/test_userdb.py @@ -3,8 +3,6 @@ This module provides tests for the functions in :py:mod:`userdbs.templatetags.userdb`. """ -from __future__ import unicode_literals - from unittest import TestCase from django.utils.translation import gettext as _ @@ -20,26 +18,22 @@ class UserdbTemplateTagTests(TestCase): """ def test_db_type_icon_class_unknown(self): - self.assertEqual( - db_type_icon_class({'db_type': 'unknown'}), - 'icon-database') + self.assertEqual(db_type_icon_class({"db_type": "unknown"}), "icon-database") def test_db_type_icon_class_mysql(self): - self.assertEqual( - db_type_icon_class({'db_type': DB_TYPES.mysql}), - 'icon-mysql') + self.assertEqual(db_type_icon_class({"db_type": DB_TYPES.mysql}), "icon-mysql") def test_db_type_icon_class_pgsql(self): self.assertEqual( - db_type_icon_class({'db_type': DB_TYPES.pgsql}), - 'icon-postgres') + db_type_icon_class({"db_type": DB_TYPES.pgsql}), "icon-postgres" + ) def test_db_type_name_mysql(self): self.assertEqual( - db_type_name({'db_type': DB_TYPES.mysql}), - _(DB_TYPES[DB_TYPES.mysql])) + db_type_name({"db_type": DB_TYPES.mysql}), _(DB_TYPES[DB_TYPES.mysql]) + ) def test_db_type_name_pgsql(self): self.assertEqual( - db_type_name({'db_type': DB_TYPES.pgsql}), - _(DB_TYPES[DB_TYPES.pgsql])) + db_type_name({"db_type": DB_TYPES.pgsql}), _(DB_TYPES[DB_TYPES.pgsql]) + ) diff --git a/gnuviechadmin/userdbs/tests/test_models.py b/gnuviechadmin/userdbs/tests/test_models.py index 41306bd..71046aa 100644 --- a/gnuviechadmin/userdbs/tests/test_models.py +++ b/gnuviechadmin/userdbs/tests/test_models.py @@ -94,7 +94,7 @@ class DatabaseUserManagerTest(TestCaseWithCeleryTasks): self.assertEqual(dbu.name, "{user}db01".format(user=self.osuser.username)) self.assertEqual(dbu.osuser, self.osuser) self.assertEqual(dbu.db_type, DB_TYPES.mysql) - taskres = TaskResult.objects.all() + taskres = TaskResult.objects.order_by("created").all() self.assertEqual(len(taskres), 4) self.assertEqual(taskres[0].creator, "handle_dbuser_created") self.assertEqual(taskres[0].notes, "mysql user creation") @@ -154,7 +154,7 @@ class DatabaseUserTest(TestCaseWithCeleryTasks): dbid = db.id self.dbu.delete() self.assertFalse(UserDatabase.objects.filter(id=dbid).exists()) - taskres = TaskResult.objects.all() + taskres = TaskResult.objects.order_by("created").all() self.assertEqual(len(taskres), 3) self.assertEqual(taskres[0].creator, "handle_userdb_created") self.assertEqual(taskres[0].notes, "pgsql database creation") @@ -187,7 +187,7 @@ class UserDatabaseManagerTest(TestCaseWithCeleryTasks): self.assertEqual( db.db_user.name, "{user}db01".format(user=self.osuser.username) ) - taskres = TaskResult.objects.all() + taskres = TaskResult.objects.order_by("created").all() self.assertEqual(len(taskres), 2) self.assertEqual(taskres[0].creator, "handle_dbuser_created") self.assertEqual(taskres[0].notes, "mysql user creation") @@ -202,7 +202,7 @@ class UserDatabaseManagerTest(TestCaseWithCeleryTasks): self.assertEqual( db.db_user.name, "{user}db01".format(user=self.osuser.username) ) - taskres = TaskResult.objects.all() + taskres = TaskResult.objects.order_by("created").all() self.assertEqual(len(taskres), 2) self.assertEqual(taskres[0].creator, "handle_dbuser_created") self.assertEqual(taskres[0].notes, "pgsql user creation") @@ -267,20 +267,20 @@ class UserDatabaseTest(TestCaseWithCeleryTasks): def test___str__(self): customer = Customer.objects.create_user(username="testcustomer") - osuser = User.objects.create_user(customer=customer) - db = UserDatabase.objects.create_userdatabase_with_user(DB_TYPES.pgsql, osuser) + os_user = User.objects.create_user(customer=customer) + db = UserDatabase.objects.create_userdatabase_with_user(DB_TYPES.pgsql, os_user) self.assertEqual( str(db), - "{user}db01 ({dbuser})".format(user=osuser.username, dbuser=db.db_user), + "{user}db01 ({dbuser})".format(user=os_user.username, dbuser=db.db_user), ) def test_delete_mysql_db(self): customer = Customer.objects.create_user(username="testcustomer") - osuser = User.objects.create_user(customer=customer) + os_user = User.objects.create_user(customer=customer) TaskResult.objects.all().delete() - db = UserDatabase.objects.create_userdatabase_with_user(DB_TYPES.mysql, osuser) + db = UserDatabase.objects.create_userdatabase_with_user(DB_TYPES.mysql, os_user) db.delete() - taskres = TaskResult.objects.all() - self.assertEqual(len(taskres), 3) - self.assertEqual(taskres[2].creator, "handle_userdb_deleted") - self.assertEqual(taskres[2].notes, "mysql database deletion") + task_result = TaskResult.objects.order_by("created").all() + self.assertEqual(len(task_result), 3) + self.assertEqual(task_result[2].creator, "handle_userdb_deleted") + self.assertEqual(task_result[2].notes, "mysql database deletion") diff --git a/gnuviechadmin/userdbs/tests/test_views.py b/gnuviechadmin/userdbs/tests/test_views.py index b52cbdf..c51662d 100644 --- a/gnuviechadmin/userdbs/tests/test_views.py +++ b/gnuviechadmin/userdbs/tests/test_views.py @@ -2,10 +2,10 @@ This module provides tests for :py:mod:`userdbs.views`. """ -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch -from django.test import TestCase from django.contrib.auth import get_user_model +from django.test import TestCase from django.urls import reverse from hostingpackages.models import ( @@ -14,11 +14,9 @@ from hostingpackages.models import ( HostingPackageTemplate, UserDatabaseOption, ) - from userdbs.models import DB_TYPES, UserDatabase from userdbs.views import AddUserDatabase, ChangeDatabaseUserPassword - User = get_user_model() TEST_USER = "test" @@ -27,7 +25,6 @@ TEST_EMAIL = "test@example.org" class HostingPackageAwareTestMixin(object): - # noinspection PyMethodMayBeStatic def _setup_hosting_package(self, customer): template = HostingPackageTemplate.objects.create( diff --git a/gnuviechadmin/userdbs/urls.py b/gnuviechadmin/userdbs/urls.py index 6aee543..4447273 100644 --- a/gnuviechadmin/userdbs/urls.py +++ b/gnuviechadmin/userdbs/urls.py @@ -2,21 +2,24 @@ This module defines the URL patterns for user database views. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import -from django.conf.urls import url +from django.urls import re_path -from .views import ( - AddUserDatabase, - ChangeDatabaseUserPassword, - DeleteUserDatabase, -) +from .views import AddUserDatabase, ChangeDatabaseUserPassword, DeleteUserDatabase urlpatterns = [ - url(r'^(?P\d+)/create$', - AddUserDatabase.as_view(), name='add_userdatabase'), - url(r'^(?P\d+)/(?P[\w0-9]+)/setpassword', - ChangeDatabaseUserPassword.as_view(), name='change_dbuser_password'), - url(r'^(?P\d+)/(?P[\w0-9]+)/delete', - DeleteUserDatabase.as_view(), name='delete_userdatabase'), + re_path( + r"^(?P\d+)/create$", AddUserDatabase.as_view(), name="add_userdatabase" + ), + re_path( + r"^(?P\d+)/(?P[\w0-9]+)/setpassword", + ChangeDatabaseUserPassword.as_view(), + name="change_dbuser_password", + ), + re_path( + r"^(?P\d+)/(?P[\w0-9]+)/delete", + DeleteUserDatabase.as_view(), + name="delete_userdatabase", + ), ] diff --git a/gnuviechadmin/userdbs/views.py b/gnuviechadmin/userdbs/views.py index d16eb5a..b5dbdd2 100644 --- a/gnuviechadmin/userdbs/views.py +++ b/gnuviechadmin/userdbs/views.py @@ -2,30 +2,19 @@ This module defines views for user database handling. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import +from django.contrib import messages from django.core.exceptions import SuspiciousOperation from django.shortcuts import redirect -from django.utils.translation import ugettext as _ -from django.views.generic.edit import ( - CreateView, - DeleteView, - UpdateView, -) -from django.contrib import messages - +from django.utils.translation import gettext as _ +from django.views.generic.edit import CreateView, DeleteView, UpdateView from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin + from gvawebcore.views import HostingPackageAndCustomerMixin -from .forms import ( - AddUserDatabaseForm, - ChangeDatabaseUserPasswordForm, -) -from .models import ( - DB_TYPES, - DatabaseUser, - UserDatabase, -) +from .forms import AddUserDatabaseForm, ChangeDatabaseUserPasswordForm +from .models import DB_TYPES, DatabaseUser, UserDatabase class AddUserDatabase( @@ -35,9 +24,10 @@ class AddUserDatabase( This view is used to setup new user databases. """ + model = UserDatabase - context_object_name = 'database' - template_name_suffix = '_create' + context_object_name = "database" + template_name_suffix = "_create" form_class = AddUserDatabaseForm def _get_dbtypes(self, hostingpackage): @@ -45,29 +35,33 @@ class AddUserDatabase( db_options = hostingpackage.get_databases() for opt in db_options: dbs_of_type = UserDatabase.objects.filter( - db_user__osuser=hostingpackage.osuser, - db_user__db_type=opt['db_type']).count() - if dbs_of_type < opt['number']: - retval.append((opt['db_type'], DB_TYPES[opt['db_type']])) + db_user__osuser=hostingpackage.osuser, db_user__db_type=opt["db_type"] + ).count() + if dbs_of_type < opt["number"]: + retval.append((opt["db_type"], DB_TYPES[opt["db_type"]])) if len(retval) < 1: raise SuspiciousOperation( - _("The hosting package has no database products assigned.")) + _("The hosting package has no database products assigned.") + ) return retval def get_form_kwargs(self): kwargs = super(AddUserDatabase, self).get_form_kwargs() - kwargs['hostingpackage'] = self.get_hosting_package() - kwargs['dbtypes'] = self._get_dbtypes(kwargs['hostingpackage']) + kwargs["hostingpackage"] = self.get_hosting_package() + kwargs["dbtypes"] = self._get_dbtypes(kwargs["hostingpackage"]) return kwargs def form_valid(self, form): userdatabase = form.save() messages.success( self.request, - _('Successfully create new {type} database {dbname} for user ' - '{dbuser}.').format( - type=userdatabase.db_user.db_type, - dbname=userdatabase.db_name, dbuser=userdatabase.db_user) + _( + "Successfully create new {type} database {dbname} for user " "{dbuser}." + ).format( + type=userdatabase.db_user.db_type, + dbname=userdatabase.db_name, + dbuser=userdatabase.db_user, + ), ) return redirect(self.get_hosting_package()) @@ -79,30 +73,31 @@ class ChangeDatabaseUserPassword( This view is used to change a database user's password. """ + model = DatabaseUser - slug_field = 'name' - context_object_name = 'dbuser' - template_name_suffix = '_setpassword' + slug_field = "name" + context_object_name = "dbuser" + template_name_suffix = "_setpassword" form_class = ChangeDatabaseUserPasswordForm def get_form_kwargs(self): kwargs = super(ChangeDatabaseUserPassword, self).get_form_kwargs() - kwargs['hostingpackage'] = self.get_hosting_package() + kwargs["hostingpackage"] = self.get_hosting_package() return kwargs def get_context_data(self, **kwargs): - context = super(ChangeDatabaseUserPassword, self).get_context_data( - **kwargs) - context['hostingpackage'] = self.get_hosting_package() - context['customer'] = self.get_customer_object() + context = super(ChangeDatabaseUserPassword, self).get_context_data(**kwargs) + context["hostingpackage"] = self.get_hosting_package() + context["customer"] = self.get_customer_object() return context def form_valid(self, form): db_user = form.save() messages.success( self.request, - _('Successfully changed password of database user {dbuser}.' - ).format(dbuser=db_user.name) + _("Successfully changed password of database user {dbuser}.").format( + dbuser=db_user.name + ), ) return redirect(self.get_hosting_package()) @@ -115,21 +110,24 @@ class DeleteUserDatabase( no more databases assigned. """ + model = UserDatabase - slug_field = 'db_name' - context_object_name = 'database' + slug_field = "db_name" + context_object_name = "database" def get_context_data(self, **kwargs): context = super(DeleteUserDatabase, self).get_context_data(**kwargs) - context.update({ - 'hostingpackage': self.get_hosting_package(), - 'customer': self.get_customer_object(), - }) + context.update( + { + "hostingpackage": self.get_hosting_package(), + "customer": self.get_customer_object(), + } + ) return context def get_success_url(self): messages.success( self.request, - _('Database deleted.'), + _("Database deleted."), ) return self.get_hosting_package().get_absolute_url() diff --git a/gnuviechadmin/websites/__init__.py b/gnuviechadmin/websites/__init__.py index 8e793c3..96f532f 100644 --- a/gnuviechadmin/websites/__init__.py +++ b/gnuviechadmin/websites/__init__.py @@ -2,4 +2,3 @@ This app takes care of websites. """ -default_app_config = 'websites.apps.WebsitesAppConfig' diff --git a/gnuviechadmin/websites/apps.py b/gnuviechadmin/websites/apps.py index 363d7ad..9b7c95e 100644 --- a/gnuviechadmin/websites/apps.py +++ b/gnuviechadmin/websites/apps.py @@ -3,9 +3,8 @@ This module contains the :py:class:`django.apps.AppConfig` instance for the :py:mod:`websites` app. """ -from __future__ import unicode_literals from django.apps import AppConfig -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import gettext_lazy as _ class WebsitesAppConfig(AppConfig): @@ -13,5 +12,14 @@ class WebsitesAppConfig(AppConfig): AppConfig for the :py:mod:`websites` app. """ - name = 'websites' - verbose_name = _('Websites') + + name = "websites" + verbose_name = _("Websites") + + def ready(self): + """ + Takes care of importing the signal handlers of the :py:mod:`websites` + app. + + """ + import websites.signals # NOQA diff --git a/gnuviechadmin/websites/forms.py b/gnuviechadmin/websites/forms.py index 94e52d1..8d253d7 100644 --- a/gnuviechadmin/websites/forms.py +++ b/gnuviechadmin/websites/forms.py @@ -2,20 +2,17 @@ This module defines form classes for website editing. """ -from __future__ import absolute_import, unicode_literals - -from django import forms -from django.urls import reverse -from django.utils.translation import ugettext as _ +from __future__ import absolute_import from crispy_forms.bootstrap import AppendedText from crispy_forms.helper import FormHelper -from crispy_forms.layout import ( - Layout, - Submit, -) +from crispy_forms.layout import Layout, Submit +from django import forms +from django.urls import reverse +from django.utils.translation import gettext as _ from domains.forms import relative_domain_validator + from .models import Website @@ -24,42 +21,40 @@ class AddWebsiteForm(forms.ModelForm): This form is used to create new Website instances. """ + class Meta: model = Website - fields = ['subdomain', 'wildcard'] + fields = ["subdomain", "wildcard"] def __init__(self, *args, **kwargs): - self.hosting_package = kwargs.pop('hostingpackage') - self.hosting_domain = kwargs.pop('domain') + self.hosting_package = kwargs.pop("hostingpackage") + self.hosting_domain = kwargs.pop("domain") super(AddWebsiteForm, self).__init__(*args, **kwargs) - self.fields['subdomain'].validators.append(relative_domain_validator) - if Website.objects.filter( - wildcard=True, domain=self.hosting_domain - ).exists(): - self.fields['wildcard'].widget = forms.HiddenInput() + self.fields["subdomain"].validators.append(relative_domain_validator) + if Website.objects.filter(wildcard=True, domain=self.hosting_domain).exists(): + self.fields["wildcard"].widget = forms.HiddenInput() self.helper = FormHelper() self.helper.form_action = reverse( - 'add_website', kwargs={ - 'package': self.hosting_package.id, - 'domain': self.hosting_domain.domain, - } + "add_website", + kwargs={ + "package": self.hosting_package.id, + "domain": self.hosting_domain.domain, + }, ) self.helper.layout = Layout( - AppendedText('subdomain', '.' + self.hosting_domain.domain), - 'wildcard', - Submit('submit', _('Add website')), + AppendedText("subdomain", "." + self.hosting_domain.domain), + "wildcard", + Submit("submit", _("Add website")), ) def clean_subdomain(self): - data = self.cleaned_data['subdomain'] - if Website.objects.filter( - domain=self.hosting_domain, subdomain=data - ).exists(): + data = self.cleaned_data["subdomain"] + if Website.objects.filter(domain=self.hosting_domain, subdomain=data).exists(): raise forms.ValidationError( - _('There is already a website for this subdomain')) - relative_domain_validator( - "{0}.{1}".format(data, self.hosting_domain.domain)) + _("There is already a website for this subdomain") + ) + relative_domain_validator("{0}.{1}".format(data, self.hosting_domain.domain)) return data def save(self, commit=True): diff --git a/gnuviechadmin/websites/locale/de/LC_MESSAGES/django.po b/gnuviechadmin/websites/locale/de/LC_MESSAGES/django.po index ad09e6f..0163d05 100644 --- a/gnuviechadmin/websites/locale/de/LC_MESSAGES/django.po +++ b/gnuviechadmin/websites/locale/de/LC_MESSAGES/django.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: websites gnuviechadmin app\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2016-01-29 11:04+0100\n" -"PO-Revision-Date: 2015-01-27 19:00+0100\n" +"POT-Creation-Date: 2023-04-16 22:07+0200\n" +"PO-Revision-Date: 2023-04-16 18:21+0200\n" "Last-Translator: Jan Dittberner \n" "Language-Team: Jan Dittberner \n" "Language: de\n" @@ -16,46 +16,46 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.6.10\n" +"X-Generator: Poedit 3.2.2\n" "X-Poedit-SourceCharset: UTF-8\n" #: websites/apps.py:17 msgid "Websites" msgstr "Webauftritte" -#: websites/forms.py:51 +#: websites/forms.py:48 msgid "Add website" msgstr "Webauftritt anlegen" -#: websites/forms.py:60 +#: websites/forms.py:55 msgid "There is already a website for this subdomain" msgstr "Es gibt bereits einen Webauftritt mit dieser Subdomain" -#: websites/models.py:35 +#: websites/models.py:32 msgid "sub domain" msgstr "Subdomain" -#: websites/models.py:37 +#: websites/models.py:34 msgid "operating system user" msgstr "Betriebssystemnutzer" -#: websites/models.py:39 +#: websites/models.py:36 msgid "domain" msgstr "Domain" -#: websites/models.py:40 +#: websites/models.py:37 msgid "wildcard" msgstr "Wildcard" -#: websites/models.py:44 +#: websites/models.py:41 msgid "website" msgstr "Webauftritt" -#: websites/models.py:45 +#: websites/models.py:42 msgid "websites" msgstr "Webauftritte" -#: websites/views.py:57 +#: websites/views.py:61 #, python-brace-format msgid "Successfully added website {subdomain}.{domain}" msgstr "Webauftritt {subdomain}.{domain} erfolgreich angelegt" diff --git a/gnuviechadmin/websites/migrations/0001_initial.py b/gnuviechadmin/websites/migrations/0001_initial.py index 51dbee9..ae67ef2 100644 --- a/gnuviechadmin/websites/migrations/0001_initial.py +++ b/gnuviechadmin/websites/migrations/0001_initial.py @@ -1,41 +1,59 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ('osusers', '0004_auto_20150104_1751'), - ('domains', '0002_auto_20150124_1909'), + ("osusers", "0004_auto_20150104_1751"), + ("domains", "0002_auto_20150124_1909"), ] operations = [ migrations.CreateModel( - name='Website', + name="Website", fields=[ - ('id', models.AutoField( - verbose_name='ID', serialize=False, auto_created=True, - primary_key=True)), - ('subdomain', models.CharField( - max_length=64, verbose_name='sub domain')), - ('wildcard', models.BooleanField( - default=False, verbose_name='wildcard')), - ('domain', models.ForeignKey( - verbose_name='domain', to='domains.HostingDomain', - on_delete=models.CASCADE)), - ('osuser', models.ForeignKey( - verbose_name='operating system user', to='osusers.User', - on_delete=models.CASCADE)), + ( + "id", + models.AutoField( + verbose_name="ID", + serialize=False, + auto_created=True, + primary_key=True, + ), + ), + ( + "subdomain", + models.CharField(max_length=64, verbose_name="sub domain"), + ), + ( + "wildcard", + models.BooleanField(default=False, verbose_name="wildcard"), + ), + ( + "domain", + models.ForeignKey( + verbose_name="domain", + to="domains.HostingDomain", + on_delete=models.CASCADE, + ), + ), + ( + "osuser", + models.ForeignKey( + verbose_name="operating system user", + to="osusers.User", + on_delete=models.CASCADE, + ), + ), ], options={ - 'verbose_name': 'website', - 'verbose_name_plural': 'websites', + "verbose_name": "website", + "verbose_name_plural": "websites", }, bases=(models.Model,), ), migrations.AlterUniqueTogether( - name='website', - unique_together={('domain', 'subdomain')}, + name="website", + unique_together={("domain", "subdomain")}, ), ] diff --git a/gnuviechadmin/websites/models.py b/gnuviechadmin/websites/models.py index a0b2168..95d5902 100644 --- a/gnuviechadmin/websites/models.py +++ b/gnuviechadmin/websites/models.py @@ -2,48 +2,32 @@ This module defines the database models for website handling. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import -from django.db import models, transaction -from django.utils.encoding import python_2_unicode_compatible -from django.utils.translation import ugettext as _ +from django.db import models +from django.utils.translation import gettext as _ from domains.models import HostingDomain from osusers.models import User as OsUser -from fileservertasks.tasks import ( - create_file_website_hierarchy, - delete_file_website_hierarchy, -) -from webtasks.tasks import ( - create_web_php_fpm_pool_config, - create_web_vhost_config, - delete_web_php_fpm_pool_config, - delete_web_vhost_config, - disable_web_vhost, - enable_web_vhost, -) - -@python_2_unicode_compatible class Website(models.Model): """ This is the model for a website. """ - subdomain = models.CharField( - _('sub domain'), max_length=64) + + subdomain = models.CharField(_("sub domain"), max_length=64) osuser = models.ForeignKey( - OsUser, verbose_name=_('operating system user'), - on_delete=models.CASCADE) - domain = models.ForeignKey( - HostingDomain, models.CASCADE, verbose_name=_('domain')) - wildcard = models.BooleanField(_('wildcard'), default=False) + OsUser, verbose_name=_("operating system user"), on_delete=models.CASCADE + ) + domain = models.ForeignKey(HostingDomain, models.CASCADE, verbose_name=_("domain")) + wildcard = models.BooleanField(_("wildcard"), default=False) class Meta: - unique_together = [('domain', 'subdomain')] - verbose_name = _('website') - verbose_name_plural = _('websites') + unique_together = [("domain", "subdomain")] + verbose_name = _("website") + verbose_name_plural = _("websites") def __str__(self): return self.get_fqdn() @@ -52,29 +36,3 @@ class Website(models.Model): return "{subdomain}.{domain}".format( subdomain=self.subdomain, domain=self.domain.domain ) - - @transaction.atomic - def save(self, *args, **kwargs): - if not self.pk: - fqdn = self.get_fqdn() - if not Website.objects.filter(osuser=self.osuser).exists(): - create_web_php_fpm_pool_config.delay( - self.osuser.username).get() - create_file_website_hierarchy.delay( - self.osuser.username, fqdn).get() - create_web_vhost_config.delay( - self.osuser.username, fqdn, self.wildcard).get() - enable_web_vhost.delay(fqdn).get() - return super(Website, self).save(*args, **kwargs) - - @transaction.atomic - def delete(self, *args, **kwargs): - fqdn = self.get_fqdn() - osuser = self.osuser - disable_web_vhost.delay(fqdn).get() - delete_web_vhost_config.delay(fqdn).get() - delete_file_website_hierarchy.delay(osuser.username, fqdn).get() - deleted = super(Website, self).delete(*args, **kwargs) - if not Website.objects.filter(osuser=osuser): - delete_web_php_fpm_pool_config.delay(osuser.username).get() - return deleted diff --git a/gnuviechadmin/websites/signals.py b/gnuviechadmin/websites/signals.py new file mode 100644 index 0000000..113eaba --- /dev/null +++ b/gnuviechadmin/websites/signals.py @@ -0,0 +1,140 @@ +""" +This module contains the signal handlers of the :py:mod:`websites` app. + +The module starts Celery_ tasks. + +.. _Celery: https://www.celeryproject.org/ + +""" +import logging + +from django.db.models.signals import post_delete, post_save +from django.dispatch import receiver + +from fileservertasks.tasks import ( + create_file_website_hierarchy, + delete_file_website_hierarchy, +) +from taskresults.models import TaskResult +from websites.models import Website +from webtasks.tasks import ( + create_web_php_fpm_pool_config, + create_web_vhost_config, + delete_web_php_fpm_pool_config, + delete_web_vhost_config, + disable_web_vhost, + enable_web_vhost, +) + +_LOGGER = logging.getLogger(__name__) + + +@receiver(post_save, sender=Website) +def handle_website_created(sender, instance, created, **kwargs): + """ + Handles post creation actions on :py:class:`Website ` instances. + + :param sender: sender of the signal + :param instance: Website instance + :param created: whether the instance has just been created + + This signal handler starts multiple Celery_ tasks. + + """ + if created: + task_result = TaskResult.objects.create_task_result( + "create_web_php_fpm_pool_config", + create_web_php_fpm_pool_config.s(instance.osuser.username), + ) + _LOGGER.info( + "PHP FPM configuration creation has been requested in task %s", + task_result.task_id, + ) + + fqdn = instance.get_fqdn() + task_result = TaskResult.objects.create_task_result( + "create_file_website_hierarchy", + create_file_website_hierarchy.s(instance.osuser.username, fqdn), + ) + _LOGGER.info( + "website file hierarchy creation for %s has been requested in task %s", + fqdn, + task_result.task_id, + ) + + task_result = TaskResult.objects.create_task_result( + "create_web_vhost_config", + create_web_vhost_config.s( + instance.osuser.username, fqdn, instance.wildcard + ), + ) + _LOGGER.info( + "virtual host configuration for %s has been requested in task %s", + fqdn, + task_result.task_id, + ) + + task_result = TaskResult.objects.create_task_result( + "enable_web_vhost", enable_web_vhost.s(fqdn) + ) + _LOGGER.info( + "virtual host enabling has been requested in task %s", task_result.task_id + ) + + _LOGGER.debug( + "website %s has been %s", instance, created and "created" or "updated" + ) + + +@receiver(post_delete, sender=Website) +def handle_website_deleted(sender, instance, **kwargs): + """ + Handles cleanup actions to be done after deletion of a :py:class:`Website ` instance. + + :param sender: sender of the signal + :param instance: Mailbox instance + + This signal handler starts multiple Celery_ tasks. + + """ + fqdn = instance.get_fqdn() + os_user = instance.osuser + + task_result = TaskResult.objects.create_task_result( + "disable_web_vhost", disable_web_vhost.s(fqdn) + ) + _LOGGER.info( + "virtual host disabling for %s has been requested in task %s", + fqdn, + task_result.task_id, + ) + + task_result = TaskResult.objects.create_task_result( + "delete_web_vhost_config", delete_web_vhost_config.s(fqdn) + ) + _LOGGER.info( + "virtual host configuration deletion for %s has been requested in task %s", + fqdn, + task_result.task_id, + ) + + task_result = TaskResult.objects.create_task_result( + "delete_file_website_hierarchy", + delete_file_website_hierarchy.s(os_user.username, fqdn), + ) + _LOGGER.info( + "website file hierarchy deletion for %s has been requested in task %s", + fqdn, + task_result.task_id, + ) + + if not Website.objects.filter(osuser=os_user): + task_result = TaskResult.objects.create_task_result( + "delete_web_php_fpm_pool_config", + delete_web_php_fpm_pool_config.s(os_user.username), + ) + _LOGGER.info( + "PHP FPM configuration deletion for OS user %s has been requested in task %s", + os_user.username, + task_result.task_id, + ) diff --git a/gnuviechadmin/websites/urls.py b/gnuviechadmin/websites/urls.py index 1fba405..54b0c10 100644 --- a/gnuviechadmin/websites/urls.py +++ b/gnuviechadmin/websites/urls.py @@ -2,19 +2,21 @@ This module defines the URL patterns for website related views. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import -from django.conf.urls import url - -from .views import ( - AddWebsite, - DeleteWebsite, -) +from django.urls import re_path +from .views import AddWebsite, DeleteWebsite urlpatterns = [ - url(r'^(?P\d+)/(?P[\w0-9.-]+)/create$', - AddWebsite.as_view(), name='add_website'), - url(r'^(?P\d+)/(?P[\w0-9.-]+)/(?P\d+)/delete$', - DeleteWebsite.as_view(), name='delete_website'), + re_path( + r"^(?P\d+)/(?P[\w0-9.-]+)/create$", + AddWebsite.as_view(), + name="add_website", + ), + re_path( + r"^(?P\d+)/(?P[\w0-9.-]+)/(?P\d+)/delete$", + DeleteWebsite.as_view(), + name="delete_website", + ), ] diff --git a/gnuviechadmin/websites/views.py b/gnuviechadmin/websites/views.py index 803386f..56bb24e 100644 --- a/gnuviechadmin/websites/views.py +++ b/gnuviechadmin/websites/views.py @@ -2,20 +2,17 @@ This module defines views for website handling. """ -from __future__ import absolute_import, unicode_literals +from __future__ import absolute_import -from django.shortcuts import get_object_or_404, redirect -from django.utils.translation import ugettext as _ -from django.views.generic.edit import ( - CreateView, - DeleteView, -) from django.contrib import messages - +from django.shortcuts import get_object_or_404, redirect +from django.utils.translation import gettext as _ +from django.views.generic.edit import CreateView, DeleteView from gvacommon.viewmixins import StaffOrSelfLoginRequiredMixin -from gvawebcore.views import HostingPackageAndCustomerMixin from domains.models import HostingDomain +from gvawebcore.views import HostingPackageAndCustomerMixin + from .forms import AddWebsiteForm from .models import Website @@ -27,36 +24,43 @@ class AddWebsite( This view is used to setup new websites for a customer hosting package. """ + model = Website - context_object_name = 'website' - template_name_suffix = '_create' + context_object_name = "website" + template_name_suffix = "_create" form_class = AddWebsiteForm def get_form_kwargs(self): kwargs = super(AddWebsite, self).get_form_kwargs() - kwargs.update({ - 'hostingpackage': self.get_hosting_package(), - 'domain': get_object_or_404( - HostingDomain, domain=self.kwargs['domain']), - }) + kwargs.update( + { + "hostingpackage": self.get_hosting_package(), + "domain": get_object_or_404( + HostingDomain, domain=self.kwargs["domain"] + ), + } + ) return kwargs def get_context_data(self, **kwargs): context = super(AddWebsite, self).get_context_data(**kwargs) - context.update({ - 'customer': self.get_customer_object(), - 'domain': get_object_or_404( - HostingDomain, domain=self.kwargs['domain']) - }) + context.update( + { + "customer": self.get_customer_object(), + "domain": get_object_or_404( + HostingDomain, domain=self.kwargs["domain"] + ), + } + ) return context def form_valid(self, form): website = form.save() messages.success( self.request, - _('Successfully added website {subdomain}.{domain}').format( + _("Successfully added website {subdomain}.{domain}").format( subdomain=website.subdomain, domain=website.domain.domain - ) + ), ) return redirect(self.get_hosting_package()) @@ -68,15 +72,18 @@ class DeleteWebsite( This view is used to delete websites in a customer hosting package. """ - context_object_name = 'website' + + context_object_name = "website" model = Website def get_context_data(self, **kwargs): context = super(DeleteWebsite, self).get_context_data(**kwargs) - context.update({ - 'customer': self.get_customer_object(), - 'hostingpackage': self.get_hosting_package(), - }) + context.update( + { + "customer": self.get_customer_object(), + "hostingpackage": self.get_hosting_package(), + } + ) return context def get_success_url(self): diff --git a/gnuviechadmin/webtasks/tasks.py b/gnuviechadmin/webtasks/tasks.py index 4abd4af..2fd1dcc 100644 --- a/gnuviechadmin/webtasks/tasks.py +++ b/gnuviechadmin/webtasks/tasks.py @@ -1,5 +1,5 @@ """ -This module defines Celery_ tasks to manage website configurations. +This module defines Celery tasks to manage website configurations. """ from __future__ import absolute_import diff --git a/gva.sh b/gva.sh index 4b0f3c2..f8c0ce4 100755 --- a/gva.sh +++ b/gva.sh @@ -7,17 +7,19 @@ DB_PORT="${GVA_PGSQL_PORT:-5432}" DB_USER="${GVA_PGSQL_USER:-gnuviechadmin}" DB_NAME="${GVA_PGSQL_DATABASE:-gnuviechadmin}" -until pg_isready -q -h "${DB_HOST}" -p "${DB_PORT}" -U "${PG_USER}" -d "${DB_NAME}" +until pg_isready -q -h "${DB_HOST}" -p "${DB_PORT}" -U "${DB_USER}" -d "${DB_NAME}" do + # shellcheck disable=SC2039 echo -n "." sleep 1 done -echo " db is ready" +echo ". db is ready" -. /home/gva/gva-venv/bin/activate +export TZ="Europe/Berlin" + +. /srv/gva/.venv/bin/activate cd /srv/gva/gnuviechadmin -python3 manage.py compilemessages python3 manage.py collectstatic --noinput python3 manage.py migrate --noinput python3 manage.py runserver 0.0.0.0:8000 diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..03f1db5 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,2262 @@ +# This file is automatically @generated by Poetry and should not be changed by hand. + +[[package]] +name = "aiohttp" +version = "3.8.4" +description = "Async http client/server framework (asyncio)" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "aiohttp-3.8.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5ce45967538fb747370308d3145aa68a074bdecb4f3a300869590f725ced69c1"}, + {file = "aiohttp-3.8.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b744c33b6f14ca26b7544e8d8aadff6b765a80ad6164fb1a430bbadd593dfb1a"}, + {file = "aiohttp-3.8.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a45865451439eb320784918617ba54b7a377e3501fb70402ab84d38c2cd891b"}, + {file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a86d42d7cba1cec432d47ab13b6637bee393a10f664c425ea7b305d1301ca1a3"}, + {file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ee3c36df21b5714d49fc4580247947aa64bcbe2939d1b77b4c8dcb8f6c9faecc"}, + {file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:176a64b24c0935869d5bbc4c96e82f89f643bcdf08ec947701b9dbb3c956b7dd"}, + {file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c844fd628851c0bc309f3c801b3a3d58ce430b2ce5b359cd918a5a76d0b20cb5"}, + {file = "aiohttp-3.8.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5393fb786a9e23e4799fec788e7e735de18052f83682ce2dfcabaf1c00c2c08e"}, + {file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e4b09863aae0dc965c3ef36500d891a3ff495a2ea9ae9171e4519963c12ceefd"}, + {file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:adfbc22e87365a6e564c804c58fc44ff7727deea782d175c33602737b7feadb6"}, + {file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:147ae376f14b55f4f3c2b118b95be50a369b89b38a971e80a17c3fd623f280c9"}, + {file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:eafb3e874816ebe2a92f5e155f17260034c8c341dad1df25672fb710627c6949"}, + {file = "aiohttp-3.8.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c6cc15d58053c76eacac5fa9152d7d84b8d67b3fde92709195cb984cfb3475ea"}, + {file = "aiohttp-3.8.4-cp310-cp310-win32.whl", hash = "sha256:59f029a5f6e2d679296db7bee982bb3d20c088e52a2977e3175faf31d6fb75d1"}, + {file = "aiohttp-3.8.4-cp310-cp310-win_amd64.whl", hash = "sha256:fe7ba4a51f33ab275515f66b0a236bcde4fb5561498fe8f898d4e549b2e4509f"}, + {file = "aiohttp-3.8.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3d8ef1a630519a26d6760bc695842579cb09e373c5f227a21b67dc3eb16cfea4"}, + {file = "aiohttp-3.8.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b3f2e06a512e94722886c0827bee9807c86a9f698fac6b3aee841fab49bbfb4"}, + {file = "aiohttp-3.8.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a80464982d41b1fbfe3154e440ba4904b71c1a53e9cd584098cd41efdb188ef"}, + {file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b631e26df63e52f7cce0cce6507b7a7f1bc9b0c501fcde69742130b32e8782f"}, + {file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f43255086fe25e36fd5ed8f2ee47477408a73ef00e804cb2b5cba4bf2ac7f5e"}, + {file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4d347a172f866cd1d93126d9b239fcbe682acb39b48ee0873c73c933dd23bd0f"}, + {file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3fec6a4cb5551721cdd70473eb009d90935b4063acc5f40905d40ecfea23e05"}, + {file = "aiohttp-3.8.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80a37fe8f7c1e6ce8f2d9c411676e4bc633a8462844e38f46156d07a7d401654"}, + {file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d1e6a862b76f34395a985b3cd39a0d949ca80a70b6ebdea37d3ab39ceea6698a"}, + {file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cd468460eefef601ece4428d3cf4562459157c0f6523db89365202c31b6daebb"}, + {file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:618c901dd3aad4ace71dfa0f5e82e88b46ef57e3239fc7027773cb6d4ed53531"}, + {file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:652b1bff4f15f6287550b4670546a2947f2a4575b6c6dff7760eafb22eacbf0b"}, + {file = "aiohttp-3.8.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80575ba9377c5171407a06d0196b2310b679dc752d02a1fcaa2bc20b235dbf24"}, + {file = "aiohttp-3.8.4-cp311-cp311-win32.whl", hash = "sha256:bbcf1a76cf6f6dacf2c7f4d2ebd411438c275faa1dc0c68e46eb84eebd05dd7d"}, + {file = "aiohttp-3.8.4-cp311-cp311-win_amd64.whl", hash = "sha256:6e74dd54f7239fcffe07913ff8b964e28b712f09846e20de78676ce2a3dc0bfc"}, + {file = "aiohttp-3.8.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:880e15bb6dad90549b43f796b391cfffd7af373f4646784795e20d92606b7a51"}, + {file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb96fa6b56bb536c42d6a4a87dfca570ff8e52de2d63cabebfd6fb67049c34b6"}, + {file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a6cadebe132e90cefa77e45f2d2f1a4b2ce5c6b1bfc1656c1ddafcfe4ba8131"}, + {file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f352b62b45dff37b55ddd7b9c0c8672c4dd2eb9c0f9c11d395075a84e2c40f75"}, + {file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ab43061a0c81198d88f39aaf90dae9a7744620978f7ef3e3708339b8ed2ef01"}, + {file = "aiohttp-3.8.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9cb1565a7ad52e096a6988e2ee0397f72fe056dadf75d17fa6b5aebaea05622"}, + {file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:1b3ea7edd2d24538959c1c1abf97c744d879d4e541d38305f9bd7d9b10c9ec41"}, + {file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:7c7837fe8037e96b6dd5cfcf47263c1620a9d332a87ec06a6ca4564e56bd0f36"}, + {file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:3b90467ebc3d9fa5b0f9b6489dfb2c304a1db7b9946fa92aa76a831b9d587e99"}, + {file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:cab9401de3ea52b4b4c6971db5fb5c999bd4260898af972bf23de1c6b5dd9d71"}, + {file = "aiohttp-3.8.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d1f9282c5f2b5e241034a009779e7b2a1aa045f667ff521e7948ea9b56e0c5ff"}, + {file = "aiohttp-3.8.4-cp36-cp36m-win32.whl", hash = "sha256:5e14f25765a578a0a634d5f0cd1e2c3f53964553a00347998dfdf96b8137f777"}, + {file = "aiohttp-3.8.4-cp36-cp36m-win_amd64.whl", hash = "sha256:4c745b109057e7e5f1848c689ee4fb3a016c8d4d92da52b312f8a509f83aa05e"}, + {file = "aiohttp-3.8.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:aede4df4eeb926c8fa70de46c340a1bc2c6079e1c40ccf7b0eae1313ffd33519"}, + {file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ddaae3f3d32fc2cb4c53fab020b69a05c8ab1f02e0e59665c6f7a0d3a5be54f"}, + {file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4eb3b82ca349cf6fadcdc7abcc8b3a50ab74a62e9113ab7a8ebc268aad35bb9"}, + {file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bcb89336efa095ea21b30f9e686763f2be4478f1b0a616969551982c4ee4c3b"}, + {file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c08e8ed6fa3d477e501ec9db169bfac8140e830aa372d77e4a43084d8dd91ab"}, + {file = "aiohttp-3.8.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c6cd05ea06daca6ad6a4ca3ba7fe7dc5b5de063ff4daec6170ec0f9979f6c332"}, + {file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7a00a9ed8d6e725b55ef98b1b35c88013245f35f68b1b12c5cd4100dddac333"}, + {file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:de04b491d0e5007ee1b63a309956eaed959a49f5bb4e84b26c8f5d49de140fa9"}, + {file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:40653609b3bf50611356e6b6554e3a331f6879fa7116f3959b20e3528783e699"}, + {file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dbf3a08a06b3f433013c143ebd72c15cac33d2914b8ea4bea7ac2c23578815d6"}, + {file = "aiohttp-3.8.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:854f422ac44af92bfe172d8e73229c270dc09b96535e8a548f99c84f82dde241"}, + {file = "aiohttp-3.8.4-cp37-cp37m-win32.whl", hash = "sha256:aeb29c84bb53a84b1a81c6c09d24cf33bb8432cc5c39979021cc0f98c1292a1a"}, + {file = "aiohttp-3.8.4-cp37-cp37m-win_amd64.whl", hash = "sha256:db3fc6120bce9f446d13b1b834ea5b15341ca9ff3f335e4a951a6ead31105480"}, + {file = "aiohttp-3.8.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fabb87dd8850ef0f7fe2b366d44b77d7e6fa2ea87861ab3844da99291e81e60f"}, + {file = "aiohttp-3.8.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:91f6d540163f90bbaef9387e65f18f73ffd7c79f5225ac3d3f61df7b0d01ad15"}, + {file = "aiohttp-3.8.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d265f09a75a79a788237d7f9054f929ced2e69eb0bb79de3798c468d8a90f945"}, + {file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d89efa095ca7d442a6d0cbc755f9e08190ba40069b235c9886a8763b03785da"}, + {file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4dac314662f4e2aa5009977b652d9b8db7121b46c38f2073bfeed9f4049732cd"}, + {file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe11310ae1e4cd560035598c3f29d86cef39a83d244c7466f95c27ae04850f10"}, + {file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ddb2a2026c3f6a68c3998a6c47ab6795e4127315d2e35a09997da21865757f8"}, + {file = "aiohttp-3.8.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e75b89ac3bd27d2d043b234aa7b734c38ba1b0e43f07787130a0ecac1e12228a"}, + {file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6e601588f2b502c93c30cd5a45bfc665faaf37bbe835b7cfd461753068232074"}, + {file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a5d794d1ae64e7753e405ba58e08fcfa73e3fad93ef9b7e31112ef3c9a0efb52"}, + {file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:a1f4689c9a1462f3df0a1f7e797791cd6b124ddbee2b570d34e7f38ade0e2c71"}, + {file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3032dcb1c35bc330134a5b8a5d4f68c1a87252dfc6e1262c65a7e30e62298275"}, + {file = "aiohttp-3.8.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8189c56eb0ddbb95bfadb8f60ea1b22fcfa659396ea36f6adcc521213cd7b44d"}, + {file = "aiohttp-3.8.4-cp38-cp38-win32.whl", hash = "sha256:33587f26dcee66efb2fff3c177547bd0449ab7edf1b73a7f5dea1e38609a0c54"}, + {file = "aiohttp-3.8.4-cp38-cp38-win_amd64.whl", hash = "sha256:e595432ac259af2d4630008bf638873d69346372d38255774c0e286951e8b79f"}, + {file = "aiohttp-3.8.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5a7bdf9e57126dc345b683c3632e8ba317c31d2a41acd5800c10640387d193ed"}, + {file = "aiohttp-3.8.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:22f6eab15b6db242499a16de87939a342f5a950ad0abaf1532038e2ce7d31567"}, + {file = "aiohttp-3.8.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7235604476a76ef249bd64cb8274ed24ccf6995c4a8b51a237005ee7a57e8643"}, + {file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea9eb976ffdd79d0e893869cfe179a8f60f152d42cb64622fca418cd9b18dc2a"}, + {file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92c0cea74a2a81c4c76b62ea1cac163ecb20fb3ba3a75c909b9fa71b4ad493cf"}, + {file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:493f5bc2f8307286b7799c6d899d388bbaa7dfa6c4caf4f97ef7521b9cb13719"}, + {file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a63f03189a6fa7c900226e3ef5ba4d3bd047e18f445e69adbd65af433add5a2"}, + {file = "aiohttp-3.8.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10c8cefcff98fd9168cdd86c4da8b84baaa90bf2da2269c6161984e6737bf23e"}, + {file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bca5f24726e2919de94f047739d0a4fc01372801a3672708260546aa2601bf57"}, + {file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:03baa76b730e4e15a45f81dfe29a8d910314143414e528737f8589ec60cf7391"}, + {file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:8c29c77cc57e40f84acef9bfb904373a4e89a4e8b74e71aa8075c021ec9078c2"}, + {file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:03543dcf98a6619254b409be2d22b51f21ec66272be4ebda7b04e6412e4b2e14"}, + {file = "aiohttp-3.8.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:17b79c2963db82086229012cff93ea55196ed31f6493bb1ccd2c62f1724324e4"}, + {file = "aiohttp-3.8.4-cp39-cp39-win32.whl", hash = "sha256:34ce9f93a4a68d1272d26030655dd1b58ff727b3ed2a33d80ec433561b03d67a"}, + {file = "aiohttp-3.8.4-cp39-cp39-win_amd64.whl", hash = "sha256:41a86a69bb63bb2fc3dc9ad5ea9f10f1c9c8e282b471931be0268ddd09430b04"}, + {file = "aiohttp-3.8.4.tar.gz", hash = "sha256:bf2e1a9162c1e441bf805a1fd166e249d574ca04e03b34f97e2928769e91ab5c"}, +] + +[package.dependencies] +aiosignal = ">=1.1.2" +async-timeout = ">=4.0.0a3,<5.0" +asynctest = {version = "0.13.0", markers = "python_version < \"3.8\""} +attrs = ">=17.3.0" +charset-normalizer = ">=2.0,<4.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["Brotli", "aiodns", "cchardet"] + +[[package]] +name = "aiosignal" +version = "1.3.1" +description = "aiosignal: a list of registered asynchronous callbacks" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, + {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, +] + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "alabaster" +version = "0.7.13" +description = "A configurable sidebar-enabled Sphinx theme" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, + {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, +] + +[[package]] +name = "amqp" +version = "5.1.1" +description = "Low-level AMQP client for Python (fork of amqplib)." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "amqp-5.1.1-py3-none-any.whl", hash = "sha256:6f0956d2c23d8fa6e7691934d8c3930eadb44972cbbd1a7ae3a520f735d43359"}, + {file = "amqp-5.1.1.tar.gz", hash = "sha256:2c1b13fecc0893e946c65cbd5f36427861cffa4ea2201d8f6fca22e2a373b5e2"}, +] + +[package.dependencies] +vine = ">=5.0.0" + +[[package]] +name = "asgiref" +version = "3.6.0" +description = "ASGI specs, helper code, and adapters" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "asgiref-3.6.0-py3-none-any.whl", hash = "sha256:71e68008da809b957b7ee4b43dbccff33d1b23519fb8344e33f049897077afac"}, + {file = "asgiref-3.6.0.tar.gz", hash = "sha256:9567dfe7bd8d3c8c892227827c41cce860b368104c3431da67a0c5a65a949506"}, +] + +[package.dependencies] +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] + +[[package]] +name = "async-timeout" +version = "4.0.2" +description = "Timeout context manager for asyncio programs" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "async-timeout-4.0.2.tar.gz", hash = "sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15"}, + {file = "async_timeout-4.0.2-py3-none-any.whl", hash = "sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c"}, +] + +[package.dependencies] +typing-extensions = {version = ">=3.6.5", markers = "python_version < \"3.8\""} + +[[package]] +name = "asynctest" +version = "0.13.0" +description = "Enhance the standard unittest package with features for testing asyncio libraries" +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "asynctest-0.13.0-py3-none-any.whl", hash = "sha256:5da6118a7e6d6b54d83a8f7197769d046922a44d2a99c21382f0a6e4fadae676"}, + {file = "asynctest-0.13.0.tar.gz", hash = "sha256:c27862842d15d83e6a34eb0b2866c323880eb3a75e4485b079ea11748fd77fac"}, +] + +[[package]] +name = "attrs" +version = "23.1.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, +] + +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] + +[[package]] +name = "babel" +version = "2.12.1" +description = "Internationalization utilities" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"}, + {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"}, +] + +[package.dependencies] +pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} + +[[package]] +name = "billiard" +version = "3.6.4.0" +description = "Python multiprocessing fork with improvements and bugfixes" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "billiard-3.6.4.0-py3-none-any.whl", hash = "sha256:87103ea78fa6ab4d5c751c4909bcff74617d985de7fa8b672cf8618afd5a875b"}, + {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"}, +] + +[[package]] +name = "black" +version = "23.3.0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, + {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, + {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, + {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, + {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, + {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, + {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, + {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, + {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, + {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, + {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, + {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, + {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, + {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, +] + +[package.dependencies] +aiohttp = {version = ">=3.7.4", optional = true, markers = "extra == \"d\""} +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "blockdiag" +version = "3.0.0" +description = "blockdiag generates block-diagram image from text" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "blockdiag-3.0.0-py3-none-any.whl", hash = "sha256:4031bfae6a7f36071d733dec639987346e10f7871356ee2c7a221961c64961d8"}, + {file = "blockdiag-3.0.0.tar.gz", hash = "sha256:dee4195bb87d23654546ba2bf5091480dbf253b409891fce2cd527c91d00a3e2"}, +] + +[package.dependencies] +funcparserlib = ">=1.0.0a0" +Pillow = ">3.0" +setuptools = "*" +webcolors = "*" + +[package.extras] +pdf = ["reportlab"] +rst = ["docutils"] +testing = ["docutils", "flake8", "flake8-coding", "flake8-copyright", "flake8-isort", "nose", "reportlab"] + +[[package]] +name = "cached-property" +version = "1.5.2" +description = "A decorator for caching properties in classes." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"}, + {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"}, +] + +[[package]] +name = "celery" +version = "5.2.7" +description = "Distributed Task Queue." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "celery-5.2.7-py3-none-any.whl", hash = "sha256:138420c020cd58d6707e6257b6beda91fd39af7afde5d36c6334d175302c0e14"}, + {file = "celery-5.2.7.tar.gz", hash = "sha256:fafbd82934d30f8a004f81e8f7a062e31413a23d444be8ee3326553915958c6d"}, +] + +[package.dependencies] +billiard = ">=3.6.4.0,<4.0" +click = ">=8.0.3,<9.0" +click-didyoumean = ">=0.0.3" +click-plugins = ">=1.1.1" +click-repl = ">=0.2.0" +importlib-metadata = {version = ">=1.4.0", markers = "python_version < \"3.8\""} +kombu = ">=5.2.3,<6.0" +pytz = ">=2021.3" +vine = ">=5.0.0,<6.0" + +[package.extras] +arangodb = ["pyArango (>=1.3.2)"] +auth = ["cryptography"] +azureblockblob = ["azure-storage-blob (==12.9.0)"] +brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"] +cassandra = ["cassandra-driver (<3.21.0)"] +consul = ["python-consul2"] +cosmosdbsql = ["pydocumentdb (==2.3.2)"] +couchbase = ["couchbase (>=3.0.0)"] +couchdb = ["pycouchdb"] +django = ["Django (>=1.11)"] +dynamodb = ["boto3 (>=1.9.178)"] +elasticsearch = ["elasticsearch"] +eventlet = ["eventlet (>=0.32.0)"] +gevent = ["gevent (>=1.5.0)"] +librabbitmq = ["librabbitmq (>=1.5.0)"] +memcache = ["pylibmc"] +mongodb = ["pymongo[srv] (>=3.11.1)"] +msgpack = ["msgpack"] +pymemcache = ["python-memcached"] +pyro = ["pyro4"] +pytest = ["pytest-celery"] +redis = ["redis (>=3.4.1,!=4.0.0,!=4.0.1)"] +s3 = ["boto3 (>=1.9.125)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +solar = ["ephem"] +sqlalchemy = ["sqlalchemy"] +sqs = ["kombu[sqs]"] +tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=1.3.1)"] +zstd = ["zstandard"] + +[[package]] +name = "certifi" +version = "2022.12.7" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, + {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, +] + +[[package]] +name = "cffi" +version = "1.15.1" +description = "Foreign Function Interface for Python calling C code." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, +] + +[package.dependencies] +pycparser = "*" + +[[package]] +name = "charset-normalizer" +version = "3.1.0" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, + {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, + {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, + {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, + {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, + {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, + {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, +] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} + +[[package]] +name = "click-didyoumean" +version = "0.3.0" +description = "Enables git-like *did-you-mean* feature in click" +category = "main" +optional = false +python-versions = ">=3.6.2,<4.0.0" +files = [ + {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"}, + {file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"}, +] + +[package.dependencies] +click = ">=7" + +[[package]] +name = "click-plugins" +version = "1.1.1" +description = "An extension module for click to enable registering CLI commands via setuptools entry-points." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, + {file = "click_plugins-1.1.1-py2.py3-none-any.whl", hash = "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"}, +] + +[package.dependencies] +click = ">=4.0" + +[package.extras] +dev = ["coveralls", "pytest (>=3.6)", "pytest-cov", "wheel"] + +[[package]] +name = "click-repl" +version = "0.2.0" +description = "REPL plugin for Click" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "click-repl-0.2.0.tar.gz", hash = "sha256:cd12f68d745bf6151210790540b4cb064c7b13e571bc64b6957d98d120dacfd8"}, + {file = "click_repl-0.2.0-py3-none-any.whl", hash = "sha256:94b3fbbc9406a236f176e0506524b2937e4b23b6f4c0c0b2a0a83f8a64e9194b"}, +] + +[package.dependencies] +click = "*" +prompt-toolkit = "*" +six = "*" + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "coverage" +version = "7.2.5" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "coverage-7.2.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:883123d0bbe1c136f76b56276074b0c79b5817dd4238097ffa64ac67257f4b6c"}, + {file = "coverage-7.2.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d2fbc2a127e857d2f8898aaabcc34c37771bf78a4d5e17d3e1f5c30cd0cbc62a"}, + {file = "coverage-7.2.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f3671662dc4b422b15776cdca89c041a6349b4864a43aa2350b6b0b03bbcc7f"}, + {file = "coverage-7.2.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780551e47d62095e088f251f5db428473c26db7829884323e56d9c0c3118791a"}, + {file = "coverage-7.2.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:066b44897c493e0dcbc9e6a6d9f8bbb6607ef82367cf6810d387c09f0cd4fe9a"}, + {file = "coverage-7.2.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b9a4ee55174b04f6af539218f9f8083140f61a46eabcaa4234f3c2a452c4ed11"}, + {file = "coverage-7.2.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:706ec567267c96717ab9363904d846ec009a48d5f832140b6ad08aad3791b1f5"}, + {file = "coverage-7.2.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ae453f655640157d76209f42c62c64c4d4f2c7f97256d3567e3b439bd5c9b06c"}, + {file = "coverage-7.2.5-cp310-cp310-win32.whl", hash = "sha256:f81c9b4bd8aa747d417407a7f6f0b1469a43b36a85748145e144ac4e8d303cb5"}, + {file = "coverage-7.2.5-cp310-cp310-win_amd64.whl", hash = "sha256:dc945064a8783b86fcce9a0a705abd7db2117d95e340df8a4333f00be5efb64c"}, + {file = "coverage-7.2.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40cc0f91c6cde033da493227797be2826cbf8f388eaa36a0271a97a332bfd7ce"}, + {file = "coverage-7.2.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a66e055254a26c82aead7ff420d9fa8dc2da10c82679ea850d8feebf11074d88"}, + {file = "coverage-7.2.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c10fbc8a64aa0f3ed136b0b086b6b577bc64d67d5581acd7cc129af52654384e"}, + {file = "coverage-7.2.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a22cbb5ede6fade0482111fa7f01115ff04039795d7092ed0db43522431b4f2"}, + {file = "coverage-7.2.5-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:292300f76440651529b8ceec283a9370532f4ecba9ad67d120617021bb5ef139"}, + {file = "coverage-7.2.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7ff8f3fb38233035028dbc93715551d81eadc110199e14bbbfa01c5c4a43f8d8"}, + {file = "coverage-7.2.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a08c7401d0b24e8c2982f4e307124b671c6736d40d1c39e09d7a8687bddf83ed"}, + {file = "coverage-7.2.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ef9659d1cda9ce9ac9585c045aaa1e59223b143f2407db0eaee0b61a4f266fb6"}, + {file = "coverage-7.2.5-cp311-cp311-win32.whl", hash = "sha256:30dcaf05adfa69c2a7b9f7dfd9f60bc8e36b282d7ed25c308ef9e114de7fc23b"}, + {file = "coverage-7.2.5-cp311-cp311-win_amd64.whl", hash = "sha256:97072cc90f1009386c8a5b7de9d4fc1a9f91ba5ef2146c55c1f005e7b5c5e068"}, + {file = "coverage-7.2.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bebea5f5ed41f618797ce3ffb4606c64a5de92e9c3f26d26c2e0aae292f015c1"}, + {file = "coverage-7.2.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:828189fcdda99aae0d6bf718ea766b2e715eabc1868670a0a07bf8404bf58c33"}, + {file = "coverage-7.2.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e8a95f243d01ba572341c52f89f3acb98a3b6d1d5d830efba86033dd3687ade"}, + {file = "coverage-7.2.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8834e5f17d89e05697c3c043d3e58a8b19682bf365048837383abfe39adaed5"}, + {file = "coverage-7.2.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d1f25ee9de21a39b3a8516f2c5feb8de248f17da7eead089c2e04aa097936b47"}, + {file = "coverage-7.2.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1637253b11a18f453e34013c665d8bf15904c9e3c44fbda34c643fbdc9d452cd"}, + {file = "coverage-7.2.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8e575a59315a91ccd00c7757127f6b2488c2f914096077c745c2f1ba5b8c0969"}, + {file = "coverage-7.2.5-cp37-cp37m-win32.whl", hash = "sha256:509ecd8334c380000d259dc66feb191dd0a93b21f2453faa75f7f9cdcefc0718"}, + {file = "coverage-7.2.5-cp37-cp37m-win_amd64.whl", hash = "sha256:12580845917b1e59f8a1c2ffa6af6d0908cb39220f3019e36c110c943dc875b0"}, + {file = "coverage-7.2.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b5016e331b75310610c2cf955d9f58a9749943ed5f7b8cfc0bb89c6134ab0a84"}, + {file = "coverage-7.2.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:373ea34dca98f2fdb3e5cb33d83b6d801007a8074f992b80311fc589d3e6b790"}, + {file = "coverage-7.2.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a063aad9f7b4c9f9da7b2550eae0a582ffc7623dca1c925e50c3fbde7a579771"}, + {file = "coverage-7.2.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38c0a497a000d50491055805313ed83ddba069353d102ece8aef5d11b5faf045"}, + {file = "coverage-7.2.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b3b05e22a77bb0ae1a3125126a4e08535961c946b62f30985535ed40e26614"}, + {file = "coverage-7.2.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0342a28617e63ad15d96dca0f7ae9479a37b7d8a295f749c14f3436ea59fdcb3"}, + {file = "coverage-7.2.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cf97ed82ca986e5c637ea286ba2793c85325b30f869bf64d3009ccc1a31ae3fd"}, + {file = "coverage-7.2.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c2c41c1b1866b670573657d584de413df701f482574bad7e28214a2362cb1fd1"}, + {file = "coverage-7.2.5-cp38-cp38-win32.whl", hash = "sha256:10b15394c13544fce02382360cab54e51a9e0fd1bd61ae9ce012c0d1e103c813"}, + {file = "coverage-7.2.5-cp38-cp38-win_amd64.whl", hash = "sha256:a0b273fe6dc655b110e8dc89b8ec7f1a778d78c9fd9b4bda7c384c8906072212"}, + {file = "coverage-7.2.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5c587f52c81211d4530fa6857884d37f514bcf9453bdeee0ff93eaaf906a5c1b"}, + {file = "coverage-7.2.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4436cc9ba5414c2c998eaedee5343f49c02ca93b21769c5fdfa4f9d799e84200"}, + {file = "coverage-7.2.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6599bf92f33ab041e36e06d25890afbdf12078aacfe1f1d08c713906e49a3fe5"}, + {file = "coverage-7.2.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:857abe2fa6a4973f8663e039ead8d22215d31db613ace76e4a98f52ec919068e"}, + {file = "coverage-7.2.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6f5cab2d7f0c12f8187a376cc6582c477d2df91d63f75341307fcdcb5d60303"}, + {file = "coverage-7.2.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:aa387bd7489f3e1787ff82068b295bcaafbf6f79c3dad3cbc82ef88ce3f48ad3"}, + {file = "coverage-7.2.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:156192e5fd3dbbcb11cd777cc469cf010a294f4c736a2b2c891c77618cb1379a"}, + {file = "coverage-7.2.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bd3b4b8175c1db502adf209d06136c000df4d245105c8839e9d0be71c94aefe1"}, + {file = "coverage-7.2.5-cp39-cp39-win32.whl", hash = "sha256:ddc5a54edb653e9e215f75de377354e2455376f416c4378e1d43b08ec50acc31"}, + {file = "coverage-7.2.5-cp39-cp39-win_amd64.whl", hash = "sha256:338aa9d9883aaaad53695cb14ccdeb36d4060485bb9388446330bef9c361c252"}, + {file = "coverage-7.2.5-pp37.pp38.pp39-none-any.whl", hash = "sha256:8877d9b437b35a85c18e3c6499b23674684bf690f5d96c1006a1ef61f9fdf0f3"}, + {file = "coverage-7.2.5.tar.gz", hash = "sha256:f99ef080288f09ffc687423b8d60978cf3a465d3f404a18d1a05474bd8575a47"}, +] + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "crispy-bootstrap5" +version = "0.7" +description = "Bootstrap5 template pack for django-crispy-forms" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "crispy-bootstrap5-0.7.tar.gz", hash = "sha256:0745a67199619149b7feca87dab7a45664876ed50fb582b38fd2aeb3f8a8d869"}, + {file = "crispy_bootstrap5-0.7-py3-none-any.whl", hash = "sha256:f3ff1ef5cb379fe80b1b02e245008f276444098a4bdb8d855bed84c623798a85"}, +] + +[package.dependencies] +django = ">=3.2" +django-crispy-forms = ">=1.13.0" + +[package.extras] +test = ["pytest", "pytest-django"] + +[[package]] +name = "cryptography" +version = "40.0.2" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cryptography-40.0.2-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:8f79b5ff5ad9d3218afb1e7e20ea74da5f76943ee5edb7f76e56ec5161ec782b"}, + {file = "cryptography-40.0.2-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:05dc219433b14046c476f6f09d7636b92a1c3e5808b9a6536adf4932b3b2c440"}, + {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4df2af28d7bedc84fe45bd49bc35d710aede676e2a4cb7fc6d103a2adc8afe4d"}, + {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dcca15d3a19a66e63662dc8d30f8036b07be851a8680eda92d079868f106288"}, + {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a04386fb7bc85fab9cd51b6308633a3c271e3d0d3eae917eebab2fac6219b6d2"}, + {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:adc0d980fd2760c9e5de537c28935cc32b9353baaf28e0814df417619c6c8c3b"}, + {file = "cryptography-40.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:d5a1bd0e9e2031465761dfa920c16b0065ad77321d8a8c1f5ee331021fda65e9"}, + {file = "cryptography-40.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a95f4802d49faa6a674242e25bfeea6fc2acd915b5e5e29ac90a32b1139cae1c"}, + {file = "cryptography-40.0.2-cp36-abi3-win32.whl", hash = "sha256:aecbb1592b0188e030cb01f82d12556cf72e218280f621deed7d806afd2113f9"}, + {file = "cryptography-40.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:b12794f01d4cacfbd3177b9042198f3af1c856eedd0a98f10f141385c809a14b"}, + {file = "cryptography-40.0.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:142bae539ef28a1c76794cca7f49729e7c54423f615cfd9b0b1fa90ebe53244b"}, + {file = "cryptography-40.0.2-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:956ba8701b4ffe91ba59665ed170a2ebbdc6fc0e40de5f6059195d9f2b33ca0e"}, + {file = "cryptography-40.0.2-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f01c9863da784558165f5d4d916093737a75203a5c5286fde60e503e4276c7a"}, + {file = "cryptography-40.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3daf9b114213f8ba460b829a02896789751626a2a4e7a43a28ee77c04b5e4958"}, + {file = "cryptography-40.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48f388d0d153350f378c7f7b41497a54ff1513c816bcbbcafe5b829e59b9ce5b"}, + {file = "cryptography-40.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c0764e72b36a3dc065c155e5b22f93df465da9c39af65516fe04ed3c68c92636"}, + {file = "cryptography-40.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:cbaba590180cba88cb99a5f76f90808a624f18b169b90a4abb40c1fd8c19420e"}, + {file = "cryptography-40.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7a38250f433cd41df7fcb763caa3ee9362777fdb4dc642b9a349721d2bf47404"}, + {file = "cryptography-40.0.2.tar.gz", hash = "sha256:c33c0d32b8594fa647d2e01dbccc303478e16fdd7cf98652d5b3ed11aa5e5c99"}, +] + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] +docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] +pep8test = ["black", "check-manifest", "mypy", "ruff"] +sdist = ["setuptools-rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-shard (>=0.1.2)", "pytest-subtests", "pytest-xdist"] +test-randomorder = ["pytest-randomly"] +tox = ["tox"] + +[[package]] +name = "defusedxml" +version = "0.7.1" +description = "XML bomb protection for Python stdlib modules" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, + {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, +] + +[[package]] +name = "django" +version = "3.2.18" +description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Django-3.2.18-py3-none-any.whl", hash = "sha256:4d492d9024c7b3dfababf49f94511ab6a58e2c9c3c7207786f1ba4eb77750706"}, + {file = "Django-3.2.18.tar.gz", hash = "sha256:08208dfe892eb64fff073ca743b3b952311104f939e7f6dae954fe72dcc533ba"}, +] + +[package.dependencies] +asgiref = ">=3.3.2,<4" +pytz = "*" +sqlparse = ">=0.2.2" + +[package.extras] +argon2 = ["argon2-cffi (>=19.1.0)"] +bcrypt = ["bcrypt"] + +[[package]] +name = "django-allauth" +version = "0.52.0" +description = "Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication." +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "django-allauth-0.52.0.tar.gz", hash = "sha256:e380661ceafe55734c40102819ae720403027036f28e9f9827f0faeddc24ed5f"}, +] + +[package.dependencies] +Django = ">=2.0" +pyjwt = {version = ">=1.7", extras = ["crypto"]} +python3-openid = ">=3.0.8" +requests = "*" +requests-oauthlib = ">=0.3.0" + +[[package]] +name = "django-crispy-forms" +version = "2.0" +description = "Best way to have Django DRY forms" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "django-crispy-forms-2.0.tar.gz", hash = "sha256:90193b068bf948d9c68449bc8260afed1a8e2afe11ee0bac8c4ebfaeb175b322"}, + {file = "django_crispy_forms-2.0-py3-none-any.whl", hash = "sha256:d1d4e585929058a9ab3b797666ea5b69320b9ba7937f9d146d32173246a6fd13"}, +] + +[package.dependencies] +django = ">=3.2" + +[[package]] +name = "django-debug-toolbar" +version = "3.8.1" +description = "A configurable set of panels that display various debug information about the current request/response." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "django_debug_toolbar-3.8.1-py3-none-any.whl", hash = "sha256:879f8a4672d41621c06a4d322dcffa630fc4df056cada6e417ed01db0e5e0478"}, + {file = "django_debug_toolbar-3.8.1.tar.gz", hash = "sha256:24ef1a7d44d25e60d7951e378454c6509bf536dce7e7d9d36e7c387db499bc27"}, +] + +[package.dependencies] +django = ">=3.2.4" +sqlparse = ">=0.2" + +[[package]] +name = "django-filter" +version = "23.2" +description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "django-filter-23.2.tar.gz", hash = "sha256:2fe15f78108475eda525692813205fa6f9e8c1caf1ae65daa5862d403c6dbf00"}, + {file = "django_filter-23.2-py3-none-any.whl", hash = "sha256:d12d8e0fc6d3eb26641e553e5d53b191eb8cec611427d4bdce0becb1f7c172b5"}, +] + +[package.dependencies] +Django = ">=3.2" + +[[package]] +name = "django-impersonate" +version = "1.9.1" +description = "Django app to allow superusers to impersonate other users." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "django-impersonate-1.9.1.tar.gz", hash = "sha256:0befdb096198b458507239a6f21574c9e0f608ab01fad352d71eb9284e5bb9c9"}, +] + +[[package]] +name = "django-model-utils" +version = "4.3.1" +description = "Django model mixins and utilities" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "django-model-utils-4.3.1.tar.gz", hash = "sha256:2e2e4f13e4f14613134a9777db7ad4265f59a1d8f1384107bcaa3028fe3c87c1"}, + {file = "django_model_utils-4.3.1-py3-none-any.whl", hash = "sha256:8c0b0177bab909a8635b602d960daa67e80607aa5469217857271a60726d7a4b"}, +] + +[package.dependencies] +Django = ">=3.2" + +[[package]] +name = "djangorestframework" +version = "3.14.0" +description = "Web APIs for Django, made easy." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "djangorestframework-3.14.0-py3-none-any.whl", hash = "sha256:eb63f58c9f218e1a7d064d17a70751f528ed4e1d35547fdade9aaf4cd103fd08"}, + {file = "djangorestframework-3.14.0.tar.gz", hash = "sha256:579a333e6256b09489cbe0a067e66abe55c6595d8926be6b99423786334350c8"}, +] + +[package.dependencies] +django = ">=3.0" +pytz = "*" + +[[package]] +name = "docutils" +version = "0.19" +description = "Docutils -- Python Documentation Utilities" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"}, + {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"}, +] + +[[package]] +name = "frozenlist" +version = "1.3.3" +description = "A list-like structure which implements collections.abc.MutableSequence" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "frozenlist-1.3.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ff8bf625fe85e119553b5383ba0fb6aa3d0ec2ae980295aaefa552374926b3f4"}, + {file = "frozenlist-1.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dfbac4c2dfcc082fcf8d942d1e49b6aa0766c19d3358bd86e2000bf0fa4a9cf0"}, + {file = "frozenlist-1.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b1c63e8d377d039ac769cd0926558bb7068a1f7abb0f003e3717ee003ad85530"}, + {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fdfc24dcfce5b48109867c13b4cb15e4660e7bd7661741a391f821f23dfdca7"}, + {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c926450857408e42f0bbc295e84395722ce74bae69a3b2aa2a65fe22cb14b99"}, + {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1841e200fdafc3d51f974d9d377c079a0694a8f06de2e67b48150328d66d5483"}, + {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f470c92737afa7d4c3aacc001e335062d582053d4dbe73cda126f2d7031068dd"}, + {file = "frozenlist-1.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:783263a4eaad7c49983fe4b2e7b53fa9770c136c270d2d4bbb6d2192bf4d9caf"}, + {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:924620eef691990dfb56dc4709f280f40baee568c794b5c1885800c3ecc69816"}, + {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ae4dc05c465a08a866b7a1baf360747078b362e6a6dbeb0c57f234db0ef88ae0"}, + {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:bed331fe18f58d844d39ceb398b77d6ac0b010d571cba8267c2e7165806b00ce"}, + {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:02c9ac843e3390826a265e331105efeab489ffaf4dd86384595ee8ce6d35ae7f"}, + {file = "frozenlist-1.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9545a33965d0d377b0bc823dcabf26980e77f1b6a7caa368a365a9497fb09420"}, + {file = "frozenlist-1.3.3-cp310-cp310-win32.whl", hash = "sha256:d5cd3ab21acbdb414bb6c31958d7b06b85eeb40f66463c264a9b343a4e238642"}, + {file = "frozenlist-1.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:b756072364347cb6aa5b60f9bc18e94b2f79632de3b0190253ad770c5df17db1"}, + {file = "frozenlist-1.3.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b4395e2f8d83fbe0c627b2b696acce67868793d7d9750e90e39592b3626691b7"}, + {file = "frozenlist-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:14143ae966a6229350021384870458e4777d1eae4c28d1a7aa47f24d030e6678"}, + {file = "frozenlist-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5d8860749e813a6f65bad8285a0520607c9500caa23fea6ee407e63debcdbef6"}, + {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23d16d9f477bb55b6154654e0e74557040575d9d19fe78a161bd33d7d76808e8"}, + {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb82dbba47a8318e75f679690190c10a5e1f447fbf9df41cbc4c3afd726d88cb"}, + {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9309869032abb23d196cb4e4db574232abe8b8be1339026f489eeb34a4acfd91"}, + {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a97b4fe50b5890d36300820abd305694cb865ddb7885049587a5678215782a6b"}, + {file = "frozenlist-1.3.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c188512b43542b1e91cadc3c6c915a82a5eb95929134faf7fd109f14f9892ce4"}, + {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:303e04d422e9b911a09ad499b0368dc551e8c3cd15293c99160c7f1f07b59a48"}, + {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:0771aed7f596c7d73444c847a1c16288937ef988dc04fb9f7be4b2aa91db609d"}, + {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:66080ec69883597e4d026f2f71a231a1ee9887835902dbe6b6467d5a89216cf6"}, + {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:41fe21dc74ad3a779c3d73a2786bdf622ea81234bdd4faf90b8b03cad0c2c0b4"}, + {file = "frozenlist-1.3.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f20380df709d91525e4bee04746ba612a4df0972c1b8f8e1e8af997e678c7b81"}, + {file = "frozenlist-1.3.3-cp311-cp311-win32.whl", hash = "sha256:f30f1928162e189091cf4d9da2eac617bfe78ef907a761614ff577ef4edfb3c8"}, + {file = "frozenlist-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:a6394d7dadd3cfe3f4b3b186e54d5d8504d44f2d58dcc89d693698e8b7132b32"}, + {file = "frozenlist-1.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8df3de3a9ab8325f94f646609a66cbeeede263910c5c0de0101079ad541af332"}, + {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0693c609e9742c66ba4870bcee1ad5ff35462d5ffec18710b4ac89337ff16e27"}, + {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd4210baef299717db0a600d7a3cac81d46ef0e007f88c9335db79f8979c0d3d"}, + {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:394c9c242113bfb4b9aa36e2b80a05ffa163a30691c7b5a29eba82e937895d5e"}, + {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6327eb8e419f7d9c38f333cde41b9ae348bec26d840927332f17e887a8dcb70d"}, + {file = "frozenlist-1.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e24900aa13212e75e5b366cb9065e78bbf3893d4baab6052d1aca10d46d944c"}, + {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3843f84a6c465a36559161e6c59dce2f2ac10943040c2fd021cfb70d58c4ad56"}, + {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:84610c1502b2461255b4c9b7d5e9c48052601a8957cd0aea6ec7a7a1e1fb9420"}, + {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c21b9aa40e08e4f63a2f92ff3748e6b6c84d717d033c7b3438dd3123ee18f70e"}, + {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:efce6ae830831ab6a22b9b4091d411698145cb9b8fc869e1397ccf4b4b6455cb"}, + {file = "frozenlist-1.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:40de71985e9042ca00b7953c4f41eabc3dc514a2d1ff534027f091bc74416401"}, + {file = "frozenlist-1.3.3-cp37-cp37m-win32.whl", hash = "sha256:180c00c66bde6146a860cbb81b54ee0df350d2daf13ca85b275123bbf85de18a"}, + {file = "frozenlist-1.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9bbbcedd75acdfecf2159663b87f1bb5cfc80e7cd99f7ddd9d66eb98b14a8411"}, + {file = "frozenlist-1.3.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:034a5c08d36649591be1cbb10e09da9f531034acfe29275fc5454a3b101ce41a"}, + {file = "frozenlist-1.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba64dc2b3b7b158c6660d49cdb1d872d1d0bf4e42043ad8d5006099479a194e5"}, + {file = "frozenlist-1.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:47df36a9fe24054b950bbc2db630d508cca3aa27ed0566c0baf661225e52c18e"}, + {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:008a054b75d77c995ea26629ab3a0c0d7281341f2fa7e1e85fa6153ae29ae99c"}, + {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:841ea19b43d438a80b4de62ac6ab21cfe6827bb8a9dc62b896acc88eaf9cecba"}, + {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e235688f42b36be2b6b06fc37ac2126a73b75fb8d6bc66dd632aa35286238703"}, + {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca713d4af15bae6e5d79b15c10c8522859a9a89d3b361a50b817c98c2fb402a2"}, + {file = "frozenlist-1.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ac5995f2b408017b0be26d4a1d7c61bce106ff3d9e3324374d66b5964325448"}, + {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a4ae8135b11652b08a8baf07631d3ebfe65a4c87909dbef5fa0cdde440444ee4"}, + {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4ea42116ceb6bb16dbb7d526e242cb6747b08b7710d9782aa3d6732bd8d27649"}, + {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:810860bb4bdce7557bc0febb84bbd88198b9dbc2022d8eebe5b3590b2ad6c842"}, + {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:ee78feb9d293c323b59a6f2dd441b63339a30edf35abcb51187d2fc26e696d13"}, + {file = "frozenlist-1.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0af2e7c87d35b38732e810befb9d797a99279cbb85374d42ea61c1e9d23094b3"}, + {file = "frozenlist-1.3.3-cp38-cp38-win32.whl", hash = "sha256:899c5e1928eec13fd6f6d8dc51be23f0d09c5281e40d9cf4273d188d9feeaf9b"}, + {file = "frozenlist-1.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:7f44e24fa70f6fbc74aeec3e971f60a14dde85da364aa87f15d1be94ae75aeef"}, + {file = "frozenlist-1.3.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2b07ae0c1edaa0a36339ec6cce700f51b14a3fc6545fdd32930d2c83917332cf"}, + {file = "frozenlist-1.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ebb86518203e12e96af765ee89034a1dbb0c3c65052d1b0c19bbbd6af8a145e1"}, + {file = "frozenlist-1.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5cf820485f1b4c91e0417ea0afd41ce5cf5965011b3c22c400f6d144296ccbc0"}, + {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c11e43016b9024240212d2a65043b70ed8dfd3b52678a1271972702d990ac6d"}, + {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8fa3c6e3305aa1146b59a09b32b2e04074945ffcfb2f0931836d103a2c38f936"}, + {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:352bd4c8c72d508778cf05ab491f6ef36149f4d0cb3c56b1b4302852255d05d5"}, + {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:65a5e4d3aa679610ac6e3569e865425b23b372277f89b5ef06cf2cdaf1ebf22b"}, + {file = "frozenlist-1.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e2c1185858d7e10ff045c496bbf90ae752c28b365fef2c09cf0fa309291669"}, + {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f163d2fd041c630fed01bc48d28c3ed4a3b003c00acd396900e11ee5316b56bb"}, + {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05cdb16d09a0832eedf770cb7bd1fe57d8cf4eaf5aced29c4e41e3f20b30a784"}, + {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:8bae29d60768bfa8fb92244b74502b18fae55a80eac13c88eb0b496d4268fd2d"}, + {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:eedab4c310c0299961ac285591acd53dc6723a1ebd90a57207c71f6e0c2153ab"}, + {file = "frozenlist-1.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3bbdf44855ed8f0fbcd102ef05ec3012d6a4fd7c7562403f76ce6a52aeffb2b1"}, + {file = "frozenlist-1.3.3-cp39-cp39-win32.whl", hash = "sha256:efa568b885bca461f7c7b9e032655c0c143d305bf01c30caf6db2854a4532b38"}, + {file = "frozenlist-1.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:cfe33efc9cb900a4c46f91a5ceba26d6df370ffddd9ca386eb1d4f0ad97b9ea9"}, + {file = "frozenlist-1.3.3.tar.gz", hash = "sha256:58bcc55721e8a90b88332d6cd441261ebb22342e238296bb330968952fbb3a6a"}, +] + +[[package]] +name = "funcparserlib" +version = "1.0.1" +description = "Recursive descent parsing library based on functional combinators" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" +files = [ + {file = "funcparserlib-1.0.1-py2.py3-none-any.whl", hash = "sha256:95da15d3f0d00b9b6f4bf04005c708af3faa115f7b45692ace064ebe758c68e8"}, + {file = "funcparserlib-1.0.1.tar.gz", hash = "sha256:a2c4a0d7942f7a0e7635c369d921066c8d4cae7f8b5bf7914466bec3c69837f4"}, +] + +[[package]] +name = "gvacommon" +version = "0.6.0" +description = "" +category = "main" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "gvacommon-0.6.0-py3-none-any.whl", hash = "sha256:89977984ae8fd76860c326b0221a3327c48b7f3349ef058541770f0d1a3a55d8"}, +] + +[package.dependencies] +django = "<4" + +[package.source] +type = "legacy" +url = "https://pypi.gnuviech-server.de/simple" +reference = "gnuviech" + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +description = "Getting image size from png/jpeg/jpeg2000/gif file" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] + +[[package]] +name = "importlib-metadata" +version = "4.13.0" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "importlib_metadata-4.13.0-py3-none-any.whl", hash = "sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116"}, + {file = "importlib_metadata-4.13.0.tar.gz", hash = "sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d"}, +] + +[package.dependencies] +typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +perf = ["ipython"] +testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] + +[[package]] +name = "isort" +version = "4.3.21" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "isort-4.3.21-py2.py3-none-any.whl", hash = "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"}, + {file = "isort-4.3.21.tar.gz", hash = "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1"}, +] + +[package.extras] +pipfile = ["pipreqs", "requirementslib"] +pyproject = ["toml"] +requirements = ["pip-api", "pipreqs"] +xdg-home = ["appdirs (>=1.4.0)"] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "kombu" +version = "5.2.4" +description = "Messaging library for Python." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "kombu-5.2.4-py3-none-any.whl", hash = "sha256:8b213b24293d3417bcf0d2f5537b7f756079e3ea232a8386dcc89a59fd2361a4"}, + {file = "kombu-5.2.4.tar.gz", hash = "sha256:37cee3ee725f94ea8bb173eaab7c1760203ea53bbebae226328600f9d2799610"}, +] + +[package.dependencies] +amqp = ">=5.0.9,<6.0.0" +cached-property = {version = "*", markers = "python_version < \"3.8\""} +importlib-metadata = {version = ">=0.18", markers = "python_version < \"3.8\""} +vine = "*" + +[package.extras] +azureservicebus = ["azure-servicebus (>=7.0.0)"] +azurestoragequeues = ["azure-storage-queue"] +consul = ["python-consul (>=0.6.0)"] +librabbitmq = ["librabbitmq (>=2.0.0)"] +mongodb = ["pymongo (>=3.3.0,<3.12.1)"] +msgpack = ["msgpack"] +pyro = ["pyro4"] +qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] +redis = ["redis (>=3.4.1,!=4.0.0,!=4.0.1)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +sqlalchemy = ["sqlalchemy"] +sqs = ["boto3 (>=1.9.12)", "pycurl (>=7.44.1,<7.45.0)", "urllib3 (>=1.26.7)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=1.3.1)"] + +[[package]] +name = "markdown" +version = "3.4.3" +description = "Python implementation of John Gruber's Markdown." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Markdown-3.4.3-py3-none-any.whl", hash = "sha256:065fd4df22da73a625f14890dd77eb8040edcbd68794bcd35943be14490608b2"}, + {file = "Markdown-3.4.3.tar.gz", hash = "sha256:8bf101198e004dc93e84a12a7395e31aac6a9c9942848ae1d99b9d72cf9b3520"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} + +[package.extras] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markupsafe" +version = "2.1.2" +description = "Safely add untrusted strings to HTML/XML markup." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, + {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, + {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, + {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, + {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, + {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, + {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, +] + +[[package]] +name = "mccabe" +version = "0.7.0" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] + +[[package]] +name = "multidict" +version = "6.0.4" +description = "multidict implementation" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b1a97283e0c85772d613878028fec909f003993e1007eafa715b24b377cb9b8"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeb6dcc05e911516ae3d1f207d4b0520d07f54484c49dfc294d6e7d63b734171"}, + {file = "multidict-6.0.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d6d635d5209b82a3492508cf5b365f3446afb65ae7ebd755e70e18f287b0adf7"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c048099e4c9e9d615545e2001d3d8a4380bd403e1a0578734e0d31703d1b0c0b"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea20853c6dbbb53ed34cb4d080382169b6f4554d394015f1bef35e881bf83547"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16d232d4e5396c2efbbf4f6d4df89bfa905eb0d4dc5b3549d872ab898451f569"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36c63aaa167f6c6b04ef2c85704e93af16c11d20de1d133e39de6a0e84582a93"}, + {file = "multidict-6.0.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:64bdf1086b6043bf519869678f5f2757f473dee970d7abf6da91ec00acb9cb98"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:43644e38f42e3af682690876cff722d301ac585c5b9e1eacc013b7a3f7b696a0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7582a1d1030e15422262de9f58711774e02fa80df0d1578995c76214f6954988"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ddff9c4e225a63a5afab9dd15590432c22e8057e1a9a13d28ed128ecf047bbdc"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ee2a1ece51b9b9e7752e742cfb661d2a29e7bcdba2d27e66e28a99f1890e4fa0"}, + {file = "multidict-6.0.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a2e4369eb3d47d2034032a26c7a80fcb21a2cb22e1173d761a162f11e562caa5"}, + {file = "multidict-6.0.4-cp310-cp310-win32.whl", hash = "sha256:574b7eae1ab267e5f8285f0fe881f17efe4b98c39a40858247720935b893bba8"}, + {file = "multidict-6.0.4-cp310-cp310-win_amd64.whl", hash = "sha256:4dcbb0906e38440fa3e325df2359ac6cb043df8e58c965bb45f4e406ecb162cc"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0dfad7a5a1e39c53ed00d2dd0c2e36aed4650936dc18fd9a1826a5ae1cad6f03"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:64da238a09d6039e3bd39bb3aee9c21a5e34f28bfa5aa22518581f910ff94af3"}, + {file = "multidict-6.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ff959bee35038c4624250473988b24f846cbeb2c6639de3602c073f10410ceba"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01a3a55bd90018c9c080fbb0b9f4891db37d148a0a18722b42f94694f8b6d4c9"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5cb09abb18c1ea940fb99360ea0396f34d46566f157122c92dfa069d3e0e982"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666daae833559deb2d609afa4490b85830ab0dfca811a98b70a205621a6109fe"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11bdf3f5e1518b24530b8241529d2050014c884cf18b6fc69c0c2b30ca248710"}, + {file = "multidict-6.0.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d18748f2d30f94f498e852c67d61261c643b349b9d2a581131725595c45ec6c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:458f37be2d9e4c95e2d8866a851663cbc76e865b78395090786f6cd9b3bbf4f4"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b1a2eeedcead3a41694130495593a559a668f382eee0727352b9a41e1c45759a"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7d6ae9d593ef8641544d6263c7fa6408cc90370c8cb2bbb65f8d43e5b0351d9c"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5979b5632c3e3534e42ca6ff856bb24b2e3071b37861c2c727ce220d80eee9ed"}, + {file = "multidict-6.0.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dcfe792765fab89c365123c81046ad4103fcabbc4f56d1c1997e6715e8015461"}, + {file = "multidict-6.0.4-cp311-cp311-win32.whl", hash = "sha256:3601a3cece3819534b11d4efc1eb76047488fddd0c85a3948099d5da4d504636"}, + {file = "multidict-6.0.4-cp311-cp311-win_amd64.whl", hash = "sha256:81a4f0b34bd92df3da93315c6a59034df95866014ac08535fc819f043bfd51f0"}, + {file = "multidict-6.0.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:67040058f37a2a51ed8ea8f6b0e6ee5bd78ca67f169ce6122f3e2ec80dfe9b78"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:853888594621e6604c978ce2a0444a1e6e70c8d253ab65ba11657659dcc9100f"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:39ff62e7d0f26c248b15e364517a72932a611a9b75f35b45be078d81bdb86603"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af048912e045a2dc732847d33821a9d84ba553f5c5f028adbd364dd4765092ac"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1e8b901e607795ec06c9e42530788c45ac21ef3aaa11dbd0c69de543bfb79a9"}, + {file = "multidict-6.0.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62501642008a8b9871ddfccbf83e4222cf8ac0d5aeedf73da36153ef2ec222d2"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:99b76c052e9f1bc0721f7541e5e8c05db3941eb9ebe7b8553c625ef88d6eefde"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:509eac6cf09c794aa27bcacfd4d62c885cce62bef7b2c3e8b2e49d365b5003fe"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21a12c4eb6ddc9952c415f24eef97e3e55ba3af61f67c7bc388dcdec1404a067"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:5cad9430ab3e2e4fa4a2ef4450f548768400a2ac635841bc2a56a2052cdbeb87"}, + {file = "multidict-6.0.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab55edc2e84460694295f401215f4a58597f8f7c9466faec545093045476327d"}, + {file = "multidict-6.0.4-cp37-cp37m-win32.whl", hash = "sha256:5a4dcf02b908c3b8b17a45fb0f15b695bf117a67b76b7ad18b73cf8e92608775"}, + {file = "multidict-6.0.4-cp37-cp37m-win_amd64.whl", hash = "sha256:6ed5f161328b7df384d71b07317f4d8656434e34591f20552c7bcef27b0ab88e"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5fc1b16f586f049820c5c5b17bb4ee7583092fa0d1c4e28b5239181ff9532e0c"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1502e24330eb681bdaa3eb70d6358e818e8e8f908a22a1851dfd4e15bc2f8161"}, + {file = "multidict-6.0.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b692f419760c0e65d060959df05f2a531945af31fda0c8a3b3195d4efd06de11"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45e1ecb0379bfaab5eef059f50115b54571acfbe422a14f668fc8c27ba410e7e"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ddd3915998d93fbcd2566ddf9cf62cdb35c9e093075f862935573d265cf8f65d"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:59d43b61c59d82f2effb39a93c48b845efe23a3852d201ed2d24ba830d0b4cf2"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc8e1d0c705233c5dd0c5e6460fbad7827d5d36f310a0fadfd45cc3029762258"}, + {file = "multidict-6.0.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6aa0418fcc838522256761b3415822626f866758ee0bc6632c9486b179d0b52"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6748717bb10339c4760c1e63da040f5f29f5ed6e59d76daee30305894069a660"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4d1a3d7ef5e96b1c9e92f973e43aa5e5b96c659c9bc3124acbbd81b0b9c8a951"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4372381634485bec7e46718edc71528024fcdc6f835baefe517b34a33c731d60"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:fc35cb4676846ef752816d5be2193a1e8367b4c1397b74a565a9d0389c433a1d"}, + {file = "multidict-6.0.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b9d9e4e2b37daddb5c23ea33a3417901fa7c7b3dee2d855f63ee67a0b21e5b1"}, + {file = "multidict-6.0.4-cp38-cp38-win32.whl", hash = "sha256:e41b7e2b59679edfa309e8db64fdf22399eec4b0b24694e1b2104fb789207779"}, + {file = "multidict-6.0.4-cp38-cp38-win_amd64.whl", hash = "sha256:d6c254ba6e45d8e72739281ebc46ea5eb5f101234f3ce171f0e9f5cc86991480"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:16ab77bbeb596e14212e7bab8429f24c1579234a3a462105cda4a66904998664"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc779e9e6f7fda81b3f9aa58e3a6091d49ad528b11ed19f6621408806204ad35"}, + {file = "multidict-6.0.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4ceef517eca3e03c1cceb22030a3e39cb399ac86bff4e426d4fc6ae49052cc60"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:281af09f488903fde97923c7744bb001a9b23b039a909460d0f14edc7bf59706"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52f2dffc8acaba9a2f27174c41c9e57f60b907bb9f096b36b1a1f3be71c6284d"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b41156839806aecb3641f3208c0dafd3ac7775b9c4c422d82ee2a45c34ba81ca"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5e3fc56f88cc98ef8139255cf8cd63eb2c586531e43310ff859d6bb3a6b51f1"}, + {file = "multidict-6.0.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8316a77808c501004802f9beebde51c9f857054a0c871bd6da8280e718444449"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f70b98cd94886b49d91170ef23ec5c0e8ebb6f242d734ed7ed677b24d50c82cf"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bf6774e60d67a9efe02b3616fee22441d86fab4c6d335f9d2051d19d90a40063"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:e69924bfcdda39b722ef4d9aa762b2dd38e4632b3641b1d9a57ca9cd18f2f83a"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:6b181d8c23da913d4ff585afd1155a0e1194c0b50c54fcfe286f70cdaf2b7176"}, + {file = "multidict-6.0.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:52509b5be062d9eafc8170e53026fbc54cf3b32759a23d07fd935fb04fc22d95"}, + {file = "multidict-6.0.4-cp39-cp39-win32.whl", hash = "sha256:27c523fbfbdfd19c6867af7346332b62b586eed663887392cff78d614f9ec313"}, + {file = "multidict-6.0.4-cp39-cp39-win_amd64.whl", hash = "sha256:33029f5734336aa0d4c0384525da0387ef89148dc7191aae00ca5fb23d7aafc2"}, + {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, +] + +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + +[[package]] +name = "oauthlib" +version = "3.2.2" +description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "oauthlib-3.2.2-py3-none-any.whl", hash = "sha256:8139f29aac13e25d502680e9e19963e83f16838d48a0d71c287fe40e7067fbca"}, + {file = "oauthlib-3.2.2.tar.gz", hash = "sha256:9859c40929662bec5d64f34d01c99e093149682a3f38915dc0655d5a633dd918"}, +] + +[package.extras] +rsa = ["cryptography (>=3.0.0)"] +signals = ["blinker (>=1.4.0)"] +signedtoken = ["cryptography (>=3.0.0)", "pyjwt (>=2.0.0,<3)"] + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "passlib" +version = "1.7.4" +description = "comprehensive password hashing framework supporting over 30 schemes" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "passlib-1.7.4-py2.py3-none-any.whl", hash = "sha256:aa6bca462b8d8bda89c70b382f0c298a20b5560af6cbfa2dce410c0a2fb669f1"}, + {file = "passlib-1.7.4.tar.gz", hash = "sha256:defd50f72b65c5402ab2c573830a6978e5f202ad0d984793c8dde2c4152ebe04"}, +] + +[package.extras] +argon2 = ["argon2-cffi (>=18.2.0)"] +bcrypt = ["bcrypt (>=3.1.0)"] +build-docs = ["cloud-sptheme (>=1.10.1)", "sphinx (>=1.6)", "sphinxcontrib-fulltoc (>=1.2.0)"] +totp = ["cryptography"] + +[[package]] +name = "pathspec" +version = "0.11.1" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, + {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, +] + +[[package]] +name = "pillow" +version = "9.5.0" +description = "Python Imaging Library (Fork)" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pillow-9.5.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:ace6ca218308447b9077c14ea4ef381ba0b67ee78d64046b3f19cf4e1139ad16"}, + {file = "Pillow-9.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3d403753c9d5adc04d4694d35cf0391f0f3d57c8e0030aac09d7678fa8030aa"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba1b81ee69573fe7124881762bb4cd2e4b6ed9dd28c9c60a632902fe8db8b38"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe7e1c262d3392afcf5071df9afa574544f28eac825284596ac6db56e6d11062"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f36397bf3f7d7c6a3abdea815ecf6fd14e7fcd4418ab24bae01008d8d8ca15e"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:252a03f1bdddce077eff2354c3861bf437c892fb1832f75ce813ee94347aa9b5"}, + {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:85ec677246533e27770b0de5cf0f9d6e4ec0c212a1f89dfc941b64b21226009d"}, + {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b416f03d37d27290cb93597335a2f85ed446731200705b22bb927405320de903"}, + {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1781a624c229cb35a2ac31cc4a77e28cafc8900733a864870c49bfeedacd106a"}, + {file = "Pillow-9.5.0-cp310-cp310-win32.whl", hash = "sha256:8507eda3cd0608a1f94f58c64817e83ec12fa93a9436938b191b80d9e4c0fc44"}, + {file = "Pillow-9.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:d3c6b54e304c60c4181da1c9dadf83e4a54fd266a99c70ba646a9baa626819eb"}, + {file = "Pillow-9.5.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:7ec6f6ce99dab90b52da21cf0dc519e21095e332ff3b399a357c187b1a5eee32"}, + {file = "Pillow-9.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:560737e70cb9c6255d6dcba3de6578a9e2ec4b573659943a5e7e4af13f298f5c"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e88745a55b88a7c64fa49bceff363a1a27d9a64e04019c2281049444a571e3"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9c206c29b46cfd343ea7cdfe1232443072bbb270d6a46f59c259460db76779a"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfcc2c53c06f2ccb8976fb5c71d448bdd0a07d26d8e07e321c103416444c7ad1"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a0f9bb6c80e6efcde93ffc51256d5cfb2155ff8f78292f074f60f9e70b942d99"}, + {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8d935f924bbab8f0a9a28404422da8af4904e36d5c33fc6f677e4c4485515625"}, + {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fed1e1cf6a42577953abbe8e6cf2fe2f566daebde7c34724ec8803c4c0cda579"}, + {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c1170d6b195555644f0616fd6ed929dfcf6333b8675fcca044ae5ab110ded296"}, + {file = "Pillow-9.5.0-cp311-cp311-win32.whl", hash = "sha256:54f7102ad31a3de5666827526e248c3530b3a33539dbda27c6843d19d72644ec"}, + {file = "Pillow-9.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfa4561277f677ecf651e2b22dc43e8f5368b74a25a8f7d1d4a3a243e573f2d4"}, + {file = "Pillow-9.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:965e4a05ef364e7b973dd17fc765f42233415974d773e82144c9bbaaaea5d089"}, + {file = "Pillow-9.5.0-cp312-cp312-win32.whl", hash = "sha256:22baf0c3cf0c7f26e82d6e1adf118027afb325e703922c8dfc1d5d0156bb2eeb"}, + {file = "Pillow-9.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:432b975c009cf649420615388561c0ce7cc31ce9b2e374db659ee4f7d57a1f8b"}, + {file = "Pillow-9.5.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5d4ebf8e1db4441a55c509c4baa7a0587a0210f7cd25fcfe74dbbce7a4bd1906"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:375f6e5ee9620a271acb6820b3d1e94ffa8e741c0601db4c0c4d3cb0a9c224bf"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99eb6cafb6ba90e436684e08dad8be1637efb71c4f2180ee6b8f940739406e78"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfaaf10b6172697b9bceb9a3bd7b951819d1ca339a5ef294d1f1ac6d7f63270"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:763782b2e03e45e2c77d7779875f4432e25121ef002a41829d8868700d119392"}, + {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:35f6e77122a0c0762268216315bf239cf52b88865bba522999dc38f1c52b9b47"}, + {file = "Pillow-9.5.0-cp37-cp37m-win32.whl", hash = "sha256:aca1c196f407ec7cf04dcbb15d19a43c507a81f7ffc45b690899d6a76ac9fda7"}, + {file = "Pillow-9.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322724c0032af6692456cd6ed554bb85f8149214d97398bb80613b04e33769f6"}, + {file = "Pillow-9.5.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a0aa9417994d91301056f3d0038af1199eb7adc86e646a36b9e050b06f526597"}, + {file = "Pillow-9.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8286396b351785801a976b1e85ea88e937712ee2c3ac653710a4a57a8da5d9c"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c830a02caeb789633863b466b9de10c015bded434deb3ec87c768e53752ad22a"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbd359831c1657d69bb81f0db962905ee05e5e9451913b18b831febfe0519082"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8fc330c3370a81bbf3f88557097d1ea26cd8b019d6433aa59f71195f5ddebbf"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:7002d0797a3e4193c7cdee3198d7c14f92c0836d6b4a3f3046a64bd1ce8df2bf"}, + {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:229e2c79c00e85989a34b5981a2b67aa079fd08c903f0aaead522a1d68d79e51"}, + {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9adf58f5d64e474bed00d69bcd86ec4bcaa4123bfa70a65ce72e424bfb88ed96"}, + {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:662da1f3f89a302cc22faa9f14a262c2e3951f9dbc9617609a47521c69dd9f8f"}, + {file = "Pillow-9.5.0-cp38-cp38-win32.whl", hash = "sha256:6608ff3bf781eee0cd14d0901a2b9cc3d3834516532e3bd673a0a204dc8615fc"}, + {file = "Pillow-9.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:e49eb4e95ff6fd7c0c402508894b1ef0e01b99a44320ba7d8ecbabefddcc5569"}, + {file = "Pillow-9.5.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:482877592e927fd263028c105b36272398e3e1be3269efda09f6ba21fd83ec66"}, + {file = "Pillow-9.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3ded42b9ad70e5f1754fb7c2e2d6465a9c842e41d178f262e08b8c85ed8a1d8e"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c446d2245ba29820d405315083d55299a796695d747efceb5717a8b450324115"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aca1152d93dcc27dc55395604dcfc55bed5f25ef4c98716a928bacba90d33a3"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:608488bdcbdb4ba7837461442b90ea6f3079397ddc968c31265c1e056964f1ef"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:60037a8db8750e474af7ffc9faa9b5859e6c6d0a50e55c45576bf28be7419705"}, + {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:07999f5834bdc404c442146942a2ecadd1cb6292f5229f4ed3b31e0a108746b1"}, + {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a127ae76092974abfbfa38ca2d12cbeddcdeac0fb71f9627cc1135bedaf9d51a"}, + {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:489f8389261e5ed43ac8ff7b453162af39c3e8abd730af8363587ba64bb2e865"}, + {file = "Pillow-9.5.0-cp39-cp39-win32.whl", hash = "sha256:9b1af95c3a967bf1da94f253e56b6286b50af23392a886720f563c547e48e964"}, + {file = "Pillow-9.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:77165c4a5e7d5a284f10a6efaa39a0ae8ba839da344f20b111d62cc932fa4e5d"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:833b86a98e0ede388fa29363159c9b1a294b0905b5128baf01db683672f230f5"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaf305d6d40bd9632198c766fb64f0c1a83ca5b667f16c1e79e1661ab5060140"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0852ddb76d85f127c135b6dd1f0bb88dbb9ee990d2cd9aa9e28526c93e794fba"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:91ec6fe47b5eb5a9968c79ad9ed78c342b1f97a091677ba0e012701add857829"}, + {file = "Pillow-9.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb841572862f629b99725ebaec3287fc6d275be9b14443ea746c1dd325053cbd"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c380b27d041209b849ed246b111b7c166ba36d7933ec6e41175fd15ab9eb1572"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c9af5a3b406a50e313467e3565fc99929717f780164fe6fbb7704edba0cebbe"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5671583eab84af046a397d6d0ba25343c00cd50bce03787948e0fff01d4fd9b1"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:84a6f19ce086c1bf894644b43cd129702f781ba5751ca8572f08aa40ef0ab7b7"}, + {file = "Pillow-9.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1e7723bd90ef94eda669a3c2c19d549874dd5badaeefabefd26053304abe5799"}, + {file = "Pillow-9.5.0.tar.gz", hash = "sha256:bf548479d336726d7a0eceb6e767e179fbde37833ae42794602631a070d630f1"}, +] + +[package.extras] +docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] +tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] + +[[package]] +name = "platformdirs" +version = "3.5.0" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.5.0-py3-none-any.whl", hash = "sha256:47692bc24c1958e8b0f13dd727307cff1db103fca36399f457da8e05f222fdc4"}, + {file = "platformdirs-3.5.0.tar.gz", hash = "sha256:7954a68d0ba23558d753f73437c55f89027cf8f5108c19844d4b82e5af396335"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.5", markers = "python_version < \"3.8\""} + +[package.extras] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] + +[[package]] +name = "prompt-toolkit" +version = "3.0.38" +description = "Library for building powerful interactive command lines in Python" +category = "main" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "prompt_toolkit-3.0.38-py3-none-any.whl", hash = "sha256:45ea77a2f7c60418850331366c81cf6b5b9cf4c7fd34616f733c5427e6abbb1f"}, + {file = "prompt_toolkit-3.0.38.tar.gz", hash = "sha256:23ac5d50538a9a38c8bde05fecb47d0b403ecd0662857a86f886f798563d5b9b"}, +] + +[package.dependencies] +wcwidth = "*" + +[[package]] +name = "psycopg2-binary" +version = "2.9.6" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "psycopg2-binary-2.9.6.tar.gz", hash = "sha256:1f64dcfb8f6e0c014c7f55e51c9759f024f70ea572fbdef123f85318c297947c"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d26e0342183c762de3276cca7a530d574d4e25121ca7d6e4a98e4f05cb8e4df7"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c48d8f2db17f27d41fb0e2ecd703ea41984ee19362cbce52c097963b3a1b4365"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffe9dc0a884a8848075e576c1de0290d85a533a9f6e9c4e564f19adf8f6e54a7"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8a76e027f87753f9bd1ab5f7c9cb8c7628d1077ef927f5e2446477153a602f2c"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6460c7a99fc939b849431f1e73e013d54aa54293f30f1109019c56a0b2b2ec2f"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae102a98c547ee2288637af07393dd33f440c25e5cd79556b04e3fca13325e5f"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9972aad21f965599ed0106f65334230ce826e5ae69fda7cbd688d24fa922415e"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7a40c00dbe17c0af5bdd55aafd6ff6679f94a9be9513a4c7e071baf3d7d22a70"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:cacbdc5839bdff804dfebc058fe25684cae322987f7a38b0168bc1b2df703fb1"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7f0438fa20fb6c7e202863e0d5ab02c246d35efb1d164e052f2f3bfe2b152bd0"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-win32.whl", hash = "sha256:b6c8288bb8a84b47e07013bb4850f50538aa913d487579e1921724631d02ea1b"}, + {file = "psycopg2_binary-2.9.6-cp310-cp310-win_amd64.whl", hash = "sha256:61b047a0537bbc3afae10f134dc6393823882eb263088c271331602b672e52e9"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:964b4dfb7c1c1965ac4c1978b0f755cc4bd698e8aa2b7667c575fb5f04ebe06b"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afe64e9b8ea66866a771996f6ff14447e8082ea26e675a295ad3bdbffdd72afb"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15e2ee79e7cf29582ef770de7dab3d286431b01c3bb598f8e05e09601b890081"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfa74c903a3c1f0d9b1c7e7b53ed2d929a4910e272add6700c38f365a6002820"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b83456c2d4979e08ff56180a76429263ea254c3f6552cd14ada95cff1dec9bb8"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0645376d399bfd64da57148694d78e1f431b1e1ee1054872a5713125681cf1be"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e99e34c82309dd78959ba3c1590975b5d3c862d6f279f843d47d26ff89d7d7e1"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4ea29fc3ad9d91162c52b578f211ff1c931d8a38e1f58e684c45aa470adf19e2"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4ac30da8b4f57187dbf449294d23b808f8f53cad6b1fc3623fa8a6c11d176dd0"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e78e6e2a00c223e164c417628572a90093c031ed724492c763721c2e0bc2a8df"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-win32.whl", hash = "sha256:1876843d8e31c89c399e31b97d4b9725a3575bb9c2af92038464231ec40f9edb"}, + {file = "psycopg2_binary-2.9.6-cp311-cp311-win_amd64.whl", hash = "sha256:b4b24f75d16a89cc6b4cdff0eb6a910a966ecd476d1e73f7ce5985ff1328e9a6"}, + {file = "psycopg2_binary-2.9.6-cp36-cp36m-win32.whl", hash = "sha256:498807b927ca2510baea1b05cc91d7da4718a0f53cb766c154c417a39f1820a0"}, + {file = "psycopg2_binary-2.9.6-cp36-cp36m-win_amd64.whl", hash = "sha256:0d236c2825fa656a2d98bbb0e52370a2e852e5a0ec45fc4f402977313329174d"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:34b9ccdf210cbbb1303c7c4db2905fa0319391bd5904d32689e6dd5c963d2ea8"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84d2222e61f313c4848ff05353653bf5f5cf6ce34df540e4274516880d9c3763"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30637a20623e2a2eacc420059be11527f4458ef54352d870b8181a4c3020ae6b"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8122cfc7cae0da9a3077216528b8bb3629c43b25053284cc868744bfe71eb141"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38601cbbfe600362c43714482f43b7c110b20cb0f8172422c616b09b85a750c5"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c7e62ab8b332147a7593a385d4f368874d5fe4ad4e341770d4983442d89603e3"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2ab652e729ff4ad76d400df2624d223d6e265ef81bb8aa17fbd63607878ecbee"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:c83a74b68270028dc8ee74d38ecfaf9c90eed23c8959fca95bd703d25b82c88e"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d4e6036decf4b72d6425d5b29bbd3e8f0ff1059cda7ac7b96d6ac5ed34ffbacd"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-win32.whl", hash = "sha256:a8c28fd40a4226b4a84bdf2d2b5b37d2c7bd49486b5adcc200e8c7ec991dfa7e"}, + {file = "psycopg2_binary-2.9.6-cp37-cp37m-win_amd64.whl", hash = "sha256:51537e3d299be0db9137b321dfb6a5022caaab275775680e0c3d281feefaca6b"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cf4499e0a83b7b7edcb8dabecbd8501d0d3a5ef66457200f77bde3d210d5debb"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7e13a5a2c01151f1208d5207e42f33ba86d561b7a89fca67c700b9486a06d0e2"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e0f754d27fddcfd74006455b6e04e6705d6c31a612ec69ddc040a5468e44b4e"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d57c3fd55d9058645d26ae37d76e61156a27722097229d32a9e73ed54819982a"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71f14375d6f73b62800530b581aed3ada394039877818b2d5f7fc77e3bb6894d"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:441cc2f8869a4f0f4bb408475e5ae0ee1f3b55b33f350406150277f7f35384fc"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:65bee1e49fa6f9cf327ce0e01c4c10f39165ee76d35c846ade7cb0ec6683e303"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:af335bac6b666cc6aea16f11d486c3b794029d9df029967f9938a4bed59b6a19"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:cfec476887aa231b8548ece2e06d28edc87c1397ebd83922299af2e051cf2827"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:65c07febd1936d63bfde78948b76cd4c2a411572a44ac50719ead41947d0f26b"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-win32.whl", hash = "sha256:4dfb4be774c4436a4526d0c554af0cc2e02082c38303852a36f6456ece7b3503"}, + {file = "psycopg2_binary-2.9.6-cp38-cp38-win_amd64.whl", hash = "sha256:02c6e3cf3439e213e4ee930308dc122d6fb4d4bea9aef4a12535fbd605d1a2fe"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e9182eb20f41417ea1dd8e8f7888c4d7c6e805f8a7c98c1081778a3da2bee3e4"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8a6979cf527e2603d349a91060f428bcb135aea2be3201dff794813256c274f1"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8338a271cb71d8da40b023a35d9c1e919eba6cbd8fa20a54b748a332c355d896"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3ed340d2b858d6e6fb5083f87c09996506af483227735de6964a6100b4e6a54"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f81e65376e52f03422e1fb475c9514185669943798ed019ac50410fb4c4df232"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfb13af3c5dd3a9588000910178de17010ebcccd37b4f9794b00595e3a8ddad3"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4c727b597c6444a16e9119386b59388f8a424223302d0c06c676ec8b4bc1f963"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d67fbdaf177da06374473ef6f7ed8cc0a9dc640b01abfe9e8a2ccb1b1402c1f"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0892ef645c2fabb0c75ec32d79f4252542d0caec1d5d949630e7d242ca4681a3"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:02c0f3757a4300cf379eb49f543fb7ac527fb00144d39246ee40e1df684ab514"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-win32.whl", hash = "sha256:c3dba7dab16709a33a847e5cd756767271697041fbe3fe97c215b1fc1f5c9848"}, + {file = "psycopg2_binary-2.9.6-cp39-cp39-win_amd64.whl", hash = "sha256:f6a88f384335bb27812293fdb11ac6aee2ca3f51d3c7820fe03de0a304ab6249"}, +] + +[[package]] +name = "pycodestyle" +version = "2.10.0" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pycodestyle-2.10.0-py2.py3-none-any.whl", hash = "sha256:8a4eaf0d0495c7395bdab3589ac2db602797d76207242c17d470186815706610"}, + {file = "pycodestyle-2.10.0.tar.gz", hash = "sha256:347187bdb476329d98f695c213d7295a846d1152ff4fe9bacb8a9590b8ee7053"}, +] + +[[package]] +name = "pycparser" +version = "2.21" +description = "C parser in Python" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, +] + +[[package]] +name = "pydocstyle" +version = "6.3.0" +description = "Python docstring style checker" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, + {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=2.0.0,<5.0.0", markers = "python_version < \"3.8\""} +snowballstemmer = ">=2.2.0" + +[package.extras] +toml = ["tomli (>=1.2.3)"] + +[[package]] +name = "pyflakes" +version = "3.0.1" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyflakes-3.0.1-py2.py3-none-any.whl", hash = "sha256:ec55bf7fe21fff7f1ad2f7da62363d749e2a470500eab1b555334b67aa1ef8cf"}, + {file = "pyflakes-3.0.1.tar.gz", hash = "sha256:ec8b276a6b60bd80defed25add7e439881c19e64850afd9b346283d4165fd0fd"}, +] + +[[package]] +name = "pygments" +version = "2.15.1" +description = "Pygments is a syntax highlighting package written in Python." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, + {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, +] + +[package.extras] +plugins = ["importlib-metadata"] + +[[package]] +name = "pyjwt" +version = "2.6.0" +description = "JSON Web Token implementation in Python" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "PyJWT-2.6.0-py3-none-any.whl", hash = "sha256:d83c3d892a77bbb74d3e1a2cfa90afaadb60945205d1095d9221f04466f64c14"}, + {file = "PyJWT-2.6.0.tar.gz", hash = "sha256:69285c7e31fc44f68a1feb309e948e0df53259d579295e6cfe2b1792329f05fd"}, +] + +[package.dependencies] +cryptography = {version = ">=3.4.0", optional = true, markers = "extra == \"crypto\""} + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] + +[[package]] +name = "pylama" +version = "8.4.1" +description = "Code audit tool for python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pylama-8.4.1-py3-none-any.whl", hash = "sha256:5bbdbf5b620aba7206d688ed9fc917ecd3d73e15ec1a89647037a09fa3a86e60"}, + {file = "pylama-8.4.1.tar.gz", hash = "sha256:2d4f7aecfb5b7466216d48610c7d6bad1c3990c29cdd392ad08259b161e486f6"}, +] + +[package.dependencies] +mccabe = ">=0.7.0" +pycodestyle = ">=2.9.1" +pydocstyle = ">=6.1.1" +pyflakes = ">=2.5.0" + +[package.extras] +all = ["eradicate", "mypy", "pylint", "radon", "vulture"] +eradicate = ["eradicate"] +mypy = ["mypy"] +pylint = ["pylint"] +radon = ["radon"] +tests = ["eradicate (>=2.0.0)", "mypy", "pylama-quotes", "pylint (>=2.11.1)", "pytest (>=7.1.2)", "pytest-mypy", "radon (>=5.1.0)", "toml", "types-setuptools", "types-toml", "vulture"] +toml = ["toml (>=0.10.2)"] +vulture = ["vulture"] + +[[package]] +name = "python-magic" +version = "0.4.27" +description = "File type identification using libmagic" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "python-magic-0.4.27.tar.gz", hash = "sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b"}, + {file = "python_magic-0.4.27-py2.py3-none-any.whl", hash = "sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3"}, +] + +[[package]] +name = "python3-openid" +version = "3.2.0" +description = "OpenID support for modern servers and consumers." +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "python3-openid-3.2.0.tar.gz", hash = "sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf"}, + {file = "python3_openid-3.2.0-py3-none-any.whl", hash = "sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b"}, +] + +[package.dependencies] +defusedxml = "*" + +[package.extras] +mysql = ["mysql-connector-python"] +postgresql = ["psycopg2"] + +[[package]] +name = "pytz" +version = "2023.3" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, + {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, +] + +[[package]] +name = "redis" +version = "4.5.4" +description = "Python client for Redis database and key-value store" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "redis-4.5.4-py3-none-any.whl", hash = "sha256:2c19e6767c474f2e85167909061d525ed65bea9301c0770bb151e041b7ac89a2"}, + {file = "redis-4.5.4.tar.gz", hash = "sha256:73ec35da4da267d6847e47f68730fdd5f62e2ca69e3ef5885c6a78a9374c3893"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.2", markers = "python_version <= \"3.11.2\""} +importlib-metadata = {version = ">=1.0", markers = "python_version < \"3.8\""} +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +hiredis = ["hiredis (>=1.0.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] + +[[package]] +name = "releases" +version = "2.1.1" +description = "A Sphinx extension for changelog manipulation" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "releases-2.1.1-py3-none-any.whl", hash = "sha256:cd06449c915ce9729aaebe19a836744998f83f9ebb0cf92e54949c88ab3402f1"}, + {file = "releases-2.1.1.tar.gz", hash = "sha256:ae0683c3f309931a3717c6976e0f079929d05d34214b432a95764e61367f58d1"}, +] + +[package.dependencies] +semantic-version = "<2.7" +sphinx = ">=4" + +[[package]] +name = "requests" +version = "2.29.0" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "requests-2.29.0-py3-none-any.whl", hash = "sha256:e8f3c9be120d3333921d213eef078af392fba3933ab7ed2d1cba3b56f2568c3b"}, + {file = "requests-2.29.0.tar.gz", hash = "sha256:f2e34a75f4749019bb0e3effb66683630e4ffeaf75819fb51bebef1bf5aef059"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "requests-oauthlib" +version = "1.3.1" +description = "OAuthlib authentication support for Requests." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "requests-oauthlib-1.3.1.tar.gz", hash = "sha256:75beac4a47881eeb94d5ea5d6ad31ef88856affe2332b9aafb52c6452ccf0d7a"}, + {file = "requests_oauthlib-1.3.1-py2.py3-none-any.whl", hash = "sha256:2577c501a2fb8d05a304c09d090d6e47c306fef15809d102b327cf8364bddab5"}, +] + +[package.dependencies] +oauthlib = ">=3.0.0" +requests = ">=2.0.0" + +[package.extras] +rsa = ["oauthlib[signedtoken] (>=3.0.0)"] + +[[package]] +name = "semantic-version" +version = "2.6.0" +description = "A library implementing the 'SemVer' scheme." +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "semantic_version-2.6.0-py3-none-any.whl", hash = "sha256:2d06ab7372034bcb8b54f2205370f4aa0643c133b7e6dbd129c5200b83ab394b"}, + {file = "semantic_version-2.6.0.tar.gz", hash = "sha256:2a4328680073e9b243667b201119772aefc5fc63ae32398d6afafff07c4f54c0"}, +] + +[[package]] +name = "setuptools" +version = "67.7.2" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-67.7.2-py3-none-any.whl", hash = "sha256:23aaf86b85ca52ceb801d32703f12d77517b2556af839621c641fca11287952b"}, + {file = "setuptools-67.7.2.tar.gz", hash = "sha256:f104fa03692a2602fa0fec6c6a9e63b6c8a968de13e17c026957dd1f53d80990"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +category = "dev" +optional = false +python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] + +[[package]] +name = "sphinx" +version = "5.3.0" +description = "Python documentation generator" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, + {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, +] + +[package.dependencies] +alabaster = ">=0.7,<0.8" +babel = ">=2.9" +colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} +docutils = ">=0.14,<0.20" +imagesize = ">=1.3" +importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} +Jinja2 = ">=3.0" +packaging = ">=21.0" +Pygments = ">=2.12" +requests = ">=2.5.0" +snowballstemmer = ">=2.0" +sphinxcontrib-applehelp = "*" +sphinxcontrib-devhelp = "*" +sphinxcontrib-htmlhelp = ">=2.0.0" +sphinxcontrib-jsmath = "*" +sphinxcontrib-qthelp = "*" +sphinxcontrib-serializinghtml = ">=1.1.5" + +[package.extras] +docs = ["sphinxcontrib-websupport"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "flake8-simplify", "isort", "mypy (>=0.981)", "sphinx-lint", "types-requests", "types-typed-ast"] +test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.2" +description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, + {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-blockdiag" +version = "3.0.0" +description = "Sphinx \"blockdiag\" extension" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sphinxcontrib-blockdiag-3.0.0.tar.gz", hash = "sha256:aa49bf924516f5de8a479994c7be81e077df5599c9da2a082003d5b388e1d450"}, + {file = "sphinxcontrib_blockdiag-3.0.0-py2.py3-none-any.whl", hash = "sha256:fc296bfb8569d5b1a6673732cd2063868c13f6f11d5b1c7cfee666297632bc06"}, +] + +[package.dependencies] +blockdiag = ">=1.5.0" +Sphinx = ">=2.0" + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.2" +description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, + {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.0" +description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, + {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["html5lib", "pytest"] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +description = "A sphinx extension which renders display math in HTML via JavaScript" +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] + +[package.extras] +test = ["flake8", "mypy", "pytest"] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.3" +description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, + {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.5" +description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." +category = "dev" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, + {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, +] + +[package.extras] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["pytest"] + +[[package]] +name = "sqlparse" +version = "0.4.4" +description = "A non-validating SQL parser." +category = "main" +optional = false +python-versions = ">=3.5" +files = [ + {file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"}, + {file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"}, +] + +[package.extras] +dev = ["build", "flake8"] +doc = ["sphinx"] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] + +[[package]] +name = "typed-ast" +version = "1.5.4" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" +optional = false +python-versions = ">=3.6" +files = [ + {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, + {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, + {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, + {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, + {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, + {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, + {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, + {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, + {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, + {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, +] + +[[package]] +name = "typing-extensions" +version = "4.5.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, + {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, +] + +[[package]] +name = "urllib3" +version = "1.26.15" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, + {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "vine" +version = "5.0.0" +description = "Promises, promises, promises." +category = "main" +optional = false +python-versions = ">=3.6" +files = [ + {file = "vine-5.0.0-py2.py3-none-any.whl", hash = "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30"}, + {file = "vine-5.0.0.tar.gz", hash = "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"}, +] + +[[package]] +name = "wcwidth" +version = "0.2.6" +description = "Measures the displayed width of unicode strings in a terminal" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, + {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, +] + +[[package]] +name = "webcolors" +version = "1.13" +description = "A library for working with the color formats defined by HTML and CSS." +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "webcolors-1.13-py3-none-any.whl", hash = "sha256:29bc7e8752c0a1bd4a1f03c14d6e6a72e93d82193738fa860cbff59d0fcc11bf"}, + {file = "webcolors-1.13.tar.gz", hash = "sha256:c225b674c83fa923be93d235330ce0300373d02885cef23238813b0d5668304a"}, +] + +[package.extras] +docs = ["furo", "sphinx", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinxext-opengraph"] +tests = ["pytest", "pytest-cov"] + +[[package]] +name = "yarl" +version = "1.9.2" +description = "Yet another URL library" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"}, + {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"}, + {file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"}, + {file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"}, + {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"}, + {file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"}, + {file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"}, + {file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"}, + {file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"}, + {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"}, + {file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"}, + {file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"}, + {file = "yarl-1.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938"}, + {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04"}, + {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582"}, + {file = "yarl-1.9.2-cp37-cp37m-win32.whl", hash = "sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b"}, + {file = "yarl-1.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368"}, + {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac"}, + {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4"}, + {file = "yarl-1.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417"}, + {file = "yarl-1.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc"}, + {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b"}, + {file = "yarl-1.9.2-cp38-cp38-win32.whl", hash = "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"}, + {file = "yarl-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"}, + {file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"}, + {file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"}, + {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"}, + {file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"}, + {file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"}, + {file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"}, +] + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" +typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} + +[[package]] +name = "zipp" +version = "3.15.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "2.0" +python-versions = "^3.7" +content-hash = "f18acd10ea383e195d41f4882109768911cd2afce7a6173de2f0e9948ea34d1a" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..4d5034f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,49 @@ +[tool.poetry] +name = "gva" +version = "0.13.0" +description = "gnuviechadmin web interface" +authors = ["Jan Dittberner "] +license = "AGPL-3+" +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.7" +django = "<4" +psycopg2-binary = "^2.9" +celery = "^5.2.7" +django-allauth = "^0.52.0" +django-crispy-forms = "^2.0" +django-debug-toolbar = "^3.8" +django-model-utils = "^4.1" +gvacommon = {version = "^0.6.0", source = "gnuviech"} +passlib = "^1.7.4" +redis = "^4.5.1" +requests-oauthlib = "^1.3.1" +django-impersonate = "^1.9.1" +djangorestframework = "^3.14.0" +markdown = "^3.4.3" +django-filter = "^23.1" +crispy-bootstrap5 = "^0.7" +python-magic = "^0.4.27" + + +[tool.poetry.group.dev.dependencies] +coverage = "^7.1.0" +sphinx = "<6" +releases = "^2.0.0" +sphinxcontrib-blockdiag = "^3.0.0" +pylama = "^8.4.1" +black = {extras = ["d"], version = "^23.3.0"} +isort = "<5" + + +[[tool.poetry.source]] +name = "gnuviech" +url = "https://pypi.gnuviech-server.de/simple" +default = false +secondary = false + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api"