diff --git a/.gitignore b/.gitignore
index 767581f76153ad5ac9f16422ee895a14b7a67f32..0faf3e4c3ecc7ff5de08a7c56ee313c488b183f6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -65,6 +65,7 @@ docs/_build/
 aleksis/node_modules/
 aleksis/static/
 aleksis/whoosh_index/
+poetry.lock
 
 .coverage
 .mypy_cache/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index af5d314dbcc470d8e5aa3791d3ebe3957f26996c..a4349087629ec1c1a04444327e503b6b775cbc4b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,6 +1,8 @@
 include:
     - project: "AlekSIS/official/AlekSIS"
       file: /ci/general.yml
+    - project: "AlekSIS/official/AlekSIS"
+      file: /ci/prepare/lock.yml
     - project: "AlekSIS/official/AlekSIS"
       file: /ci/test/test.yml
     - project: "AlekSIS/official/AlekSIS"
diff --git a/.mailmap b/.mailmap
index f813351a7160f9a16f64e1e26868a139ce60ae56..c41b8aad5213d38c135d502ffc643f79a036bcf6 100644
--- a/.mailmap
+++ b/.mailmap
@@ -8,7 +8,9 @@ Jonathan Weth <git@jonathanweth.de> Jonathan Weth <joniweth@gmx.de>
 Jonathan Weth <git@jonathanweth.de> Jonathan Weth <mail@jonathanweth.de>
 Jonathan Weth <git@jonathanweth.de> Jonathan Weth <wethjo@katharineum.de>
 Julian Leucker <leuckerj@gmail.com> Julian <leuckerj@gmail.com>
+Lloyd Meins <git@lloydmeins.de> Aithus <lloydmeins@gmx.net>
 Silas Della Contrada <s.developer@4-dc.de> sdcieo0330 <silasdc0@gmail.com>
+Tom Teichler <tom.teichler@teckids.org> Tom Teichler <t.teichler@babiel.com>
 mirabilos <thorsten.glaser@teckids.org> mirabilos <mirabilos@evolvis.org>
 mirabilos <thorsten.glaser@teckids.org> mirabilos <t.glaser@tarent.de>
 root (Skolelinux) <root@tjener.intern> root <root@tjener.intern>
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 5cd079bca115d28e7caccf0566ead90ac2b66fca..e79c84b72de33640303c9b1f01044e5d3521c9b7 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -12,14 +12,132 @@ Unreleased
 Added
 ~~~~~
 
+* Add preference for configuring the default phone number country code.
+
+Added
+~~~~~
+
+* OpenID Connect RSA keys can now be passed as string in config files
+* Views filtering for person names now also search the username of a linked user
+* OAuth2 applications now take an icon which is shown in the authorization progress.
+* Add support for hiding the main side nav in ``base.html``.
+
+Fixed
+~~~~~
+
+* GroupManager.get_queryset() returned an incomplete QuerySet
+* OAuth was broken by a non-semver-adhering django-oauth-toolkit update
+* Too long texts in chips didn't result in a larger chip.
+* The ``Person`` model had an ``is_active`` flag that was used in unclear ways; it is now removed
+* The data check results list view didn't work if a related object had been deleted in the meanwhile.
+* Socialaccount login template was not overriden
+
+Changed
+~~~~~~~
+
+* Configuration files are now deep merged by default
+* Improvements for shell_plus module loading
+
+  * core.Group model now takes precedence over auth.Group
+  * Name collisions are resolved by prefixing with the app label
+  * Apps can extend SHELL_PLUS_APP_PREFIXES and SHELL_PLUS_DONT_LOAD
+
+* [Docker] Base image now contains curl, grep, less, sed, and pspg
+* Views raising a 404 error can now customise the message that is displayed on the error page
+* OpenID Connect is enabled by default now, without RSA support
+* Login and authorization pages for OAuth2/OpenID Connect now indicate that the user is in progress
+  to authorize an external application.
+
+`2.5`_ – 2022-01-02
+-------------------
+
+Added
+~~~~~
+
+* Recursive helper methods for group hierarchies
+
+Fixed
+~~~~~
+
+* Remove left-over reference to preferences in a form definition that caused
+  form extensions in downstream apps to break
+* Allow non-LDAP users to authenticate if LDAP is used with password handling
+* Additional button on progress page for background tasks was shown even if the task failed.
+* Register preference for available allowed oauth grants.
+
+`2.4`_ – 2021-12-24
+-------------------
+
+Added
+~~~~~
+
+* Allow configuration of database options
+* User invitations with invite codes and targeted invites for existing
+  persons
+
+Fixed
+~~~~~
+
+* Correctly update theme colours on change again
+* Use correct favicon as default AlekSIS favicon
+* Show all years in a 200 year range around the current year in date pickers
+* Imprint is now called "Imprint" and not "Impress".
+* Logo files weren't uploaded to public namespace.
+* Limit LDAP network timeouts to not hang indefinitely on login if LDAP
+  server is unreachable
+
+Changed
+~~~~~~~
+
+* Modified the appearance of tables for mobile users to be more user friendly
+* [Dev] Remove lock file; locking dependencies is the distribution's
+  responsibility
+
+Removed
+~~~~~~~
+
+* Remove old generated AlekSIS icons
+
+`2.3.1`_ – 2021-12-17
+---------------------
+
+Fixed
+~~~~~
+
+* Small files could fail to upload to S3 storage due to MemoryFileUploadHandler
+* Corrected typos in previous changelog
+
+`2.3`_ – 2021-12-15
+-------------------
+
+Added
+~~~~~
+
 * [OAuth] Allow apps to fill in their own claim data matching their scopes
 
 Fixed
 ~~~~~
 
 * View for assigning permissions didn't work with some global permissions.
+* PDFs generated in background didn't contain logo or site title.
+* Admins were redirected to their user preferences
+  while they wanted to edit the preferences of another user.
+* Some CharFields were using NULL values in database when field is empty
+* Optional dependecy `sentry-sdk` was not optional
+
+Changed
+~~~~~~~
+
+* Docker base image ships PostgreSQL 14 client binaries for maximum compatibility
+* Docker base image contains Sentry client by default (disabled in config by default)
+
+Removed
+~~~~~~~
+
+* Remove impersonation page. Use the impersonation button on the person
+  detail view instead.
 
-`2.2.1_ – 2021-12-02
+`2.2.1`_ – 2021-12-02
 --------------------
 
 Fixed
@@ -514,3 +632,7 @@ Fixed
 .. _2.1.1: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.1.1
 .. _2.2: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.2
 .. _2.2.1: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.2.1
+.. _2.3: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.3
+.. _2.3.1: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.3.1
+.. _2.4: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.4
+.. _2.5: https://edugit.org/AlekSIS/Official/AlekSIS/-/tags/2.5
diff --git a/Dockerfile b/Dockerfile
index a44a0ce8021f06d34e39c7d6c594602cb623f690..acb08f98ffe79dccc32f048590bb123c2763302f 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,7 +1,7 @@
 FROM debian:bullseye-slim AS core
 
 # Build arguments
-ARG EXTRAS="ldap,s3"
+ARG EXTRAS="ldap,s3,sentry"
 ARG APP_VERSION=""
 
 # Configure Python to be nice inside Docker and pip to stfu
@@ -19,20 +19,25 @@ ENV ALEKSIS_static__root /usr/share/aleksis/static
 ENV ALEKSIS_media__root /var/lib/aleksis/media
 ENV ALEKSIS_backup__location /var/lib/aleksis/backups
 ENV ALEKSIS_dev__uwsgi__celery false
+ENV PSQL_PAGER=pspg
 
 # Install necessary Debian and PyPI packages for build and runtime
 RUN apt-get -y update && \
-    apt-get -y install eatmydata && \
+    apt-get -y install eatmydata gnupg postgresql-common && \
+    /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && \
     eatmydata apt-get -y upgrade && \
     eatmydata apt-get install -y --no-install-recommends \
         build-essential \
         chromium \
+        curl \
 	dumb-init \
 	gettext \
-	libpq5 \
+        grep \
+        less \
 	libpq-dev \
 	libssl-dev \
-	postgresql-client \
+	postgresql-client-14 \
+        pspg \
 	python3-dev \
 	python3-magic \
 	python3-pip \
@@ -76,6 +81,7 @@ RUN set -e; \
     eatmydata apt-get remove --purge -y \
         build-essential \
         gettext \
+        gnupg \
         libpq-dev \
         libssl-dev \
         libldap2-dev \
diff --git a/README.rst b/README.rst
index 20780a642a74b8560bc21d1137193e78f7681642..2ffdcdca8d82845a1d3cdf838d10ca1290118e1e 100644
--- a/README.rst
+++ b/README.rst
@@ -6,7 +6,7 @@ This is the core of the AlekSIS framework and the official distribution
 developers and administrators.
 
 If you are looking for the AlekSIS standard distribution, i.e. the complete
-software product ready for installation and usage, please visit the `AlekSIS`_
+software product ready for installation and usage, please visit the `AlekSIS®`_
 website or the distribution repository on `EduGit`_.
 
 Features
@@ -83,7 +83,7 @@ AlekSIS® is a registered trademark of the AlekSIS open source project, represen
 by Teckids e.V. Please refer to the `trademark policy`_ for hints on using the trademark
 AlekSIS®.
 
-.. _AlekSIS: https://aleksis.org
+.. _AlekSIS®: https://aleksis.org
 .. _European Union Public Licence: https://eupl.eu/
 .. _EduGit: https://edugit.org/AlekSIS/official/AlekSIS
 .. _trademark policy: https://aleksis.org/pages/about
diff --git a/aleksis/core/filters.py b/aleksis/core/filters.py
index eb5724b322232381017aa0b84e4ab111a9304eda..0bccc2664489f3698fc77bbe7299a9210ed81dac 100644
--- a/aleksis/core/filters.py
+++ b/aleksis/core/filters.py
@@ -54,6 +54,7 @@ class PersonFilter(FilterSet):
             "additional_name__icontains",
             "last_name__icontains",
             "short_name__icontains",
+            "user__username__icontains",
         ],
         label=_("Search by name"),
     )
@@ -72,11 +73,11 @@ class PersonFilter(FilterSet):
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
-        self.form.layout = Layout(Row("name", "contact"), Row("is_active", "sex", "primary_group"))
+        self.form.layout = Layout(Row("name", "contact"), Row("sex", "primary_group"))
 
     class Meta:
         model = Person
-        fields = ["sex", "is_active", "primary_group"]
+        fields = ["sex", "primary_group"]
 
 
 class PermissionFilter(FilterSet):
diff --git a/aleksis/core/forms.py b/aleksis/core/forms.py
index 7d32fd45b29dc4338fbdc1aa4e971e3015695715..459d4d8bb067f5915a41677ff3f941caa1de154d 100644
--- a/aleksis/core/forms.py
+++ b/aleksis/core/forms.py
@@ -6,7 +6,7 @@ from django.conf import settings
 from django.contrib.auth import get_user_model
 from django.contrib.auth.models import Permission
 from django.contrib.sites.models import Site
-from django.core.exceptions import ValidationError
+from django.core.exceptions import SuspiciousOperation, ValidationError
 from django.db.models import QuerySet
 from django.http import HttpRequest
 from django.utils.translation import gettext_lazy as _
@@ -14,6 +14,7 @@ from django.utils.translation import gettext_lazy as _
 from allauth.account.adapter import get_adapter
 from allauth.account.forms import SignupForm
 from allauth.account.utils import setup_user_email
+from dj_cleavejs import CleaveWidget
 from django_select2.forms import ModelSelect2MultipleWidget, ModelSelect2Widget, Select2Widget
 from dynamic_preferences.forms import PreferenceForm
 from guardian.shortcuts import assign_perm
@@ -47,7 +48,6 @@ class PersonForm(ExtensibleForm):
             _("Base data"),
             "short_name",
             Row("user", "primary_group"),
-            "is_active",
             Row("first_name", "additional_name", "last_name"),
         ),
         Fieldset(_("Address"), Row("street", "housenumber"), Row("postal_code", "place")),
@@ -66,7 +66,6 @@ class PersonForm(ExtensibleForm):
         model = Person
         fields = [
             "user",
-            "is_active",
             "first_name",
             "last_name",
             "additional_name",
@@ -401,6 +400,27 @@ DashboardWidgetOrderFormSet = forms.formset_factory(
 )
 
 
+class InvitationCodeForm(forms.Form):
+    """Form to enter an invitation code."""
+
+    code = forms.CharField(
+        label=_("Invitation code"),
+        help_text=_("Please enter your invitation code."),
+    )
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        # Calculate number of fields
+        length = get_site_preferences()["auth__invite_code_length"]
+        packet_size = get_site_preferences()["auth__invite_code_packet_size"]
+        blocks = [
+            packet_size,
+        ] * length
+
+        self.fields["code"].widget = CleaveWidget(blocks=blocks, delimiter="-", uppercase=True)
+
+
 class SelectPermissionForm(forms.Form):
     """Select a permission to assign."""
 
@@ -521,13 +541,42 @@ class AccountRegisterForm(SignupForm, ExtensibleForm):
     """Form to register new user accounts."""
 
     class Meta:
-        model = Group
-        fields = []
+        model = Person
+        fields = [
+            "first_name",
+            "additional_name",
+            "last_name",
+            "street",
+            "housenumber",
+            "postal_code",
+            "place",
+            "date_of_birth",
+            "place_of_birth",
+            "sex",
+            "photo",
+            "mobile_number",
+            "phone_number",
+            "short_name",
+            "description",
+        ]
 
     layout = Layout(
         Fieldset(
             _("Base data"),
-            Row("first_name", "last_name"),
+            Row("first_name", "additional_name", "last_name"),
+            "short_name",
+        ),
+        Fieldset(
+            _("Adress data"),
+            Row("street", "housenumber"),
+            Row("postal_code", "place"),
+        ),
+        Fieldset(_("Contact data"), Row("mobile_number", "phone_number")),
+        Fieldset(
+            _("Additional data"),
+            Row("date_of_birth", "place_of_birth"),
+            Row("sex", "photo"),
+            "description",
         ),
         Fieldset(
             _("Account data"),
@@ -535,72 +584,46 @@ class AccountRegisterForm(SignupForm, ExtensibleForm):
             Row("email", "email2"),
             Row("password1", "password2"),
         ),
-        Fieldset(
-            _("Consents"),
-            Row("privacy_policy"),
-        ),
     )
 
-    def __init__(self, *args, **kwargs):
-        super(AccountRegisterForm, self).__init__(*args, **kwargs)
-        self.fields["password1"] = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
-
-        privacy_policy = get_site_preferences()["footer__privacy_url"]
-
-        if settings.SIGNUP_PASSWORD_ENTER_TWICE:
-            self.fields["password2"] = forms.CharField(
-                label=_("Password (again)"), widget=forms.PasswordInput
-            )
-
-        self.fields["first_name"] = forms.CharField(
-            required=True,
-        )
+    password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
 
-        self.fields["last_name"] = forms.CharField(
-            required=True,
-        )
+    if settings.SIGNUP_PASSWORD_ENTER_TWICE:
+        password2 = forms.CharField(label=_("Password (again)"), widget=forms.PasswordInput)
 
-        self.fields["privacy_policy"] = forms.BooleanField(
-            help_text=_(
-                f"I have read the <a href='{privacy_policy}'>Privacy policy</a>"
-                " and agree with them."
-            ),
-            required=True,
-        )
+    def __init__(self, *args, **kwargs):
+        request = kwargs.pop("request", None)
+        super(AccountRegisterForm, self).__init__(*args, **kwargs)
 
-    def clean(self):
-        super(AccountRegisterForm, self).clean()
+        if request.session.get("account_verified_email"):
+            email = request.session["account_verified_email"]
 
-        dummy_user = get_user_model()
-        password = self.cleaned_data.get("password1")
-        if password:
             try:
-                get_adapter().clean_password(password, user=dummy_user)
-            except forms.ValidationError as e:
-                self.add_error("password1", e)
+                person = Person.objects.get(email=email)
+            except (Person.DoesNotExist, Person.MultipleObjectsReturned):
+                raise SuspiciousOperation()
 
-        if (
-            settings.SIGNUP_PASSWORD_ENTER_TWICE
-            and "password1" in self.cleaned_data
-            and "password2" in self.cleaned_data
-        ):
-            if self.cleaned_data["password1"] != self.cleaned_data["password2"]:
-                self.add_error(
-                    "password2",
-                    _("You must type the same password each time."),
-                )
-        return self.cleaned_data
+            self.fields["email"].disabled = True
+            self.fields["email2"].disabled = True
+
+            if person:
+                available_fields = [field.name for field in Person._meta.get_fields()]
+                for field in self.fields:
+                    if field in available_fields and getattr(person, field):
+                        self.fields[field].disabled = True
+                        self.fields[field].initial = getattr(person, field)
 
     def save(self, request):
         adapter = get_adapter(request)
         user = adapter.new_user(request)
         adapter.save_user(request, user, self)
-        Person.objects.create(
-            first_name=self.cleaned_data["first_name"],
-            last_name=self.cleaned_data["last_name"],
-            email=self.cleaned_data["email"],
-            user=user,
-        )
+        # Create person
+        data = {}
+        for field in Person._meta.get_fields():
+            if field.name in self.cleaned_data:
+                data[field.name] = self.cleaned_data[field.name]
+        if not Person.objects.filter(email=data["email"]):
+            _person, created = Person.objects.update_or_create(user=user, **data)
         self.custom_signup(request, user)
         setup_user_email(request, user, [])
         return user
@@ -758,6 +781,7 @@ class OAuthApplicationForm(forms.ModelForm):
         model = OAuthApplication
         fields = (
             "name",
+            "icon",
             "client_id",
             "client_secret",
             "client_type",
diff --git a/aleksis/core/locale/ar/LC_MESSAGES/django.po b/aleksis/core/locale/ar/LC_MESSAGES/django.po
index 33b5acc1cc5ba743104df96de5f85d64a90010c1..1ec59a49caf44641b442796d8e1ec3ebd502344d 100644
--- a/aleksis/core/locale/ar/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/ar/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: AlekSIS (School Information System) 0.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-11-29 09:59+0100\n"
+"POT-Creation-Date: 2021-12-28 12:13+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,26 +18,33 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
 
-#: aleksis/core/apps.py:151
+#: aleksis/core/apps.py:152
 msgid "OpenID Connect scope"
 msgstr ""
 
-#: aleksis/core/apps.py:152
+#: aleksis/core/apps.py:153
 msgid "Given name, family name, link to profile and picture if existing."
 msgstr ""
 
-#: aleksis/core/apps.py:153
+#: aleksis/core/apps.py:154
 msgid "Full home postal address"
 msgstr ""
 
-#: aleksis/core/apps.py:154
+#: aleksis/core/apps.py:155
 msgid "Email address"
 msgstr ""
 
-#: aleksis/core/apps.py:155
+#: aleksis/core/apps.py:156
 msgid "Home and mobile phone"
 msgstr ""
 
+#: aleksis/core/apps.py:157 aleksis/core/forms.py:223 aleksis/core/menus.py:265
+#: aleksis/core/models.py:414 aleksis/core/templates/core/group/list.html:8
+#: aleksis/core/templates/core/group/list.html:9
+#: aleksis/core/templates/core/person/full.html:152
+msgid "Groups"
+msgstr ""
+
 #: aleksis/core/data_checks.py:55
 msgid "Ignore problem"
 msgstr ""
@@ -84,156 +91,148 @@ msgstr ""
 msgid "Content type"
 msgstr ""
 
-#: aleksis/core/filters.py:112 aleksis/core/models.py:579
+#: aleksis/core/filters.py:112 aleksis/core/models.py:581
 msgid "User"
 msgstr ""
 
-#: aleksis/core/filters.py:134 aleksis/core/models.py:411
+#: aleksis/core/filters.py:134 aleksis/core/models.py:413
 msgid "Group"
 msgstr ""
 
-#: aleksis/core/forms.py:46 aleksis/core/forms.py:514
+#: aleksis/core/forms.py:48 aleksis/core/forms.py:559
 msgid "Base data"
 msgstr ""
 
-#: aleksis/core/forms.py:52
+#: aleksis/core/forms.py:54
 msgid "Address"
 msgstr ""
 
-#: aleksis/core/forms.py:53
+#: aleksis/core/forms.py:55 aleksis/core/forms.py:568
 msgid "Contact data"
 msgstr ""
 
-#: aleksis/core/forms.py:55
+#: aleksis/core/forms.py:57
 msgid "Advanced personal data"
 msgstr ""
 
-#: aleksis/core/forms.py:103
+#: aleksis/core/forms.py:105
 msgid "New user"
 msgstr ""
 
-#: aleksis/core/forms.py:103
+#: aleksis/core/forms.py:105
 msgid "Create a new account"
 msgstr ""
 
-#: aleksis/core/forms.py:134
+#: aleksis/core/forms.py:136
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr ""
 
-#: aleksis/core/forms.py:138
+#: aleksis/core/forms.py:140
 msgid "This username is already in use."
 msgstr ""
 
-#: aleksis/core/forms.py:155 aleksis/core/models.py:125
+#: aleksis/core/forms.py:157 aleksis/core/models.py:129
 msgid "School term"
 msgstr ""
 
-#: aleksis/core/forms.py:156
+#: aleksis/core/forms.py:158
 msgid "Common data"
 msgstr ""
 
-#: aleksis/core/forms.py:157 aleksis/core/forms.py:208
-#: aleksis/core/menus.py:256 aleksis/core/models.py:148
+#: aleksis/core/forms.py:159 aleksis/core/forms.py:210
+#: aleksis/core/menus.py:254 aleksis/core/models.py:152
 #: aleksis/core/templates/core/person/list.html:8
 #: aleksis/core/templates/core/person/list.html:9
 msgid "Persons"
 msgstr ""
 
-#: aleksis/core/forms.py:158
+#: aleksis/core/forms.py:160 aleksis/core/forms.py:570
 msgid "Additional data"
 msgstr ""
 
-#: aleksis/core/forms.py:200 aleksis/core/forms.py:203
-#: aleksis/core/models.py:68
+#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
+#: aleksis/core/models.py:72
 msgid "Date"
 msgstr ""
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:76
+#: aleksis/core/forms.py:203 aleksis/core/forms.py:206
+#: aleksis/core/models.py:80
 msgid "Time"
 msgstr ""
 
-#: aleksis/core/forms.py:221 aleksis/core/menus.py:267
-#: aleksis/core/models.py:412 aleksis/core/templates/core/group/list.html:8
-#: aleksis/core/templates/core/group/list.html:9
-#: aleksis/core/templates/core/person/full.html:145
-msgid "Groups"
-msgstr ""
-
-#: aleksis/core/forms.py:234
+#: aleksis/core/forms.py:236
 msgid "From when until when should the announcement be displayed?"
 msgstr ""
 
-#: aleksis/core/forms.py:237
+#: aleksis/core/forms.py:239
 msgid "Who should see the announcement?"
 msgstr ""
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:240
 msgid "Write your announcement:"
 msgstr ""
 
-#: aleksis/core/forms.py:277
+#: aleksis/core/forms.py:279
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr ""
 
-#: aleksis/core/forms.py:281
+#: aleksis/core/forms.py:283
 msgid "The from date and time must be earlier then the until date and time."
 msgstr ""
 
-#: aleksis/core/forms.py:290
+#: aleksis/core/forms.py:292
 msgid "You need at least one recipient."
 msgstr ""
 
-#: aleksis/core/forms.py:411
+#: aleksis/core/forms.py:401
+msgid "Invitation code"
+msgstr ""
+
+#: aleksis/core/forms.py:402
+msgid "Please enter your invitation code."
+msgstr ""
+
+#: aleksis/core/forms.py:434
 msgid "Who should get the permission?"
 msgstr ""
 
-#: aleksis/core/forms.py:412
+#: aleksis/core/forms.py:435
 msgid "On what?"
 msgstr ""
 
-#: aleksis/core/forms.py:438
+#: aleksis/core/forms.py:461
 msgid "Select objects which the permission should be granted for:"
 msgstr ""
 
-#: aleksis/core/forms.py:441
+#: aleksis/core/forms.py:464
 msgid "Grant the permission for all objects"
 msgstr ""
 
-#: aleksis/core/forms.py:449
+#: aleksis/core/forms.py:472
 msgid "You must select at least one group or person which should get the permission."
 msgstr ""
 
-#: aleksis/core/forms.py:454
+#: aleksis/core/forms.py:477
 msgid "You must grant the permission to all objects and/or to some objects."
 msgstr ""
 
-#: aleksis/core/forms.py:518
-msgid "Account data"
+#: aleksis/core/forms.py:564
+msgid "Adress data"
 msgstr ""
 
-#: aleksis/core/forms.py:524
-msgid "Consents"
+#: aleksis/core/forms.py:576
+msgid "Account data"
 msgstr ""
 
-#: aleksis/core/forms.py:531
+#: aleksis/core/forms.py:583
 msgid "Password"
 msgstr ""
 
-#: aleksis/core/forms.py:537
+#: aleksis/core/forms.py:586
 msgid "Password (again)"
 msgstr ""
 
-#: aleksis/core/forms.py:550
-#, python-brace-format
-msgid "I have read the <a href='{privacy_policy}'>Privacy policy</a> and agree with them."
-msgstr ""
-
-#: aleksis/core/forms.py:575
-msgid "You must type the same password each time."
-msgstr ""
-
-#: aleksis/core/forms.py:720
+#: aleksis/core/forms.py:752
 msgid "No valid selection."
 msgstr ""
 
@@ -241,24 +240,20 @@ msgstr ""
 msgid "There are unresolved data problems."
 msgstr ""
 
-#: aleksis/core/health_checks.py:38
-msgid "The backup folder doesn't exist."
-msgstr ""
-
-#: aleksis/core/health_checks.py:47
+#: aleksis/core/health_checks.py:44
 #, python-brace-format
 msgid "Last backup {time_gone_since_backup}!"
 msgstr ""
 
-#: aleksis/core/health_checks.py:49
+#: aleksis/core/health_checks.py:46
 msgid "No backup found!"
 msgstr ""
 
-#: aleksis/core/health_checks.py:76
+#: aleksis/core/health_checks.py:73
 msgid "No backup result found!"
 msgstr ""
 
-#: aleksis/core/health_checks.py:78
+#: aleksis/core/health_checks.py:75
 #, python-brace-format
 msgid "{task.status} - {task.result}"
 msgstr ""
@@ -274,34 +269,38 @@ msgstr ""
 msgid "Sign up"
 msgstr ""
 
-#: aleksis/core/menus.py:24
+#: aleksis/core/menus.py:24 aleksis/core/templates/invitations/enter.html:7
+msgid "Accept invitation"
+msgstr ""
+
+#: aleksis/core/menus.py:33
 msgid "Dashboard"
 msgstr ""
 
-#: aleksis/core/menus.py:32 aleksis/core/models.py:625
-#: aleksis/core/preferences.py:27
+#: aleksis/core/menus.py:41 aleksis/core/models.py:627
+#: aleksis/core/preferences.py:28
 #: aleksis/core/templates/core/notifications.html:4
 #: aleksis/core/templates/core/notifications.html:5
 msgid "Notifications"
 msgstr ""
 
-#: aleksis/core/menus.py:44
+#: aleksis/core/menus.py:53
 msgid "Account"
 msgstr ""
 
-#: aleksis/core/menus.py:51
+#: aleksis/core/menus.py:60
 msgid "Stop impersonation"
 msgstr ""
 
-#: aleksis/core/menus.py:60 aleksis/core/templates/core/base.html:80
+#: aleksis/core/menus.py:69 aleksis/core/templates/core/base.html:80
 msgid "Logout"
 msgstr ""
 
-#: aleksis/core/menus.py:66
+#: aleksis/core/menus.py:75
 msgid "2FA"
 msgstr ""
 
-#: aleksis/core/menus.py:74
+#: aleksis/core/menus.py:83
 #: aleksis/core/templates/account/password_change.html:5
 #: aleksis/core/templates/account/password_change.html:6
 #: aleksis/core/templates/account/password_change.html:19
@@ -313,830 +312,850 @@ msgstr ""
 msgid "Change password"
 msgstr ""
 
-#: aleksis/core/menus.py:86
+#: aleksis/core/menus.py:95
 msgid "Me"
 msgstr ""
 
-#: aleksis/core/menus.py:95
+#: aleksis/core/menus.py:104
 #: aleksis/core/templates/dynamic_preferences/form.html:5
 msgid "Preferences"
 msgstr ""
 
-#: aleksis/core/menus.py:104
+#: aleksis/core/menus.py:113
 msgid "Third-party accounts"
 msgstr ""
 
-#: aleksis/core/menus.py:113
+#: aleksis/core/menus.py:122
 #: aleksis/core/templates/oauth2_provider/authorized-tokens.html:5
 #: aleksis/core/templates/oauth2_provider/authorized-tokens.html:6
 msgid "Authorized applications"
 msgstr ""
 
-#: aleksis/core/menus.py:124
+#: aleksis/core/menus.py:133
 msgid "Admin"
 msgstr ""
 
-#: aleksis/core/menus.py:132 aleksis/core/models.py:725
+#: aleksis/core/menus.py:141 aleksis/core/models.py:727
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr ""
 
-#: aleksis/core/menus.py:143 aleksis/core/models.py:126
+#: aleksis/core/menus.py:152 aleksis/core/models.py:130
 #: aleksis/core/templates/core/school_term/list.html:8
 #: aleksis/core/templates/core/school_term/list.html:9
 msgid "School terms"
 msgstr ""
 
-#: aleksis/core/menus.py:154
+#: aleksis/core/menus.py:163
 #: aleksis/core/templates/core/dashboard_widget/list.html:8
 #: aleksis/core/templates/core/dashboard_widget/list.html:9
 msgid "Dashboard widgets"
 msgstr ""
 
-#: aleksis/core/menus.py:165
+#: aleksis/core/menus.py:174
 #: aleksis/core/templates/core/management/data_management.html:6
 #: aleksis/core/templates/core/management/data_management.html:7
 msgid "Data management"
 msgstr ""
 
-#: aleksis/core/menus.py:176
+#: aleksis/core/menus.py:185
 #: aleksis/core/templates/core/pages/system_status.html:5
 #: aleksis/core/templates/core/pages/system_status.html:7
 msgid "System status"
 msgstr ""
 
-#: aleksis/core/menus.py:187
-msgid "Impersonation"
-msgstr ""
-
-#: aleksis/core/menus.py:198
+#: aleksis/core/menus.py:196
 msgid "Configuration"
 msgstr ""
 
-#: aleksis/core/menus.py:209 aleksis/core/templates/core/data_check/list.html:9
+#: aleksis/core/menus.py:207 aleksis/core/templates/core/data_check/list.html:9
 #: aleksis/core/templates/core/data_check/list.html:10
 msgid "Data checks"
 msgstr ""
 
-#: aleksis/core/menus.py:215 aleksis/core/templates/core/perms/list.html:13
+#: aleksis/core/menus.py:213 aleksis/core/templates/core/perms/list.html:13
 #: aleksis/core/templates/core/perms/list.html:14
 msgid "Manage permissions"
 msgstr ""
 
-#: aleksis/core/menus.py:226
+#: aleksis/core/menus.py:224
 msgid "Backend Admin"
 msgstr ""
 
-#: aleksis/core/menus.py:234
+#: aleksis/core/menus.py:232
 #: aleksis/core/templates/oauth2_provider/application/list.html:5
 #: aleksis/core/templates/oauth2_provider/application/list.html:6
 msgid "OAuth2 Applications"
 msgstr ""
 
-#: aleksis/core/menus.py:247
+#: aleksis/core/menus.py:245
 msgid "People"
 msgstr ""
 
-#: aleksis/core/menus.py:278 aleksis/core/models.py:979
+#: aleksis/core/menus.py:276 aleksis/core/models.py:981
 #: aleksis/core/templates/core/group_type/list.html:8
 #: aleksis/core/templates/core/group_type/list.html:9
 msgid "Group types"
 msgstr ""
 
-#: aleksis/core/menus.py:289
+#: aleksis/core/menus.py:287
 msgid "Groups and child groups"
 msgstr ""
 
-#: aleksis/core/menus.py:300 aleksis/core/models.py:460
+#: aleksis/core/menus.py:298 aleksis/core/models.py:462
 #: aleksis/core/templates/core/additional_field/list.html:8
 #: aleksis/core/templates/core/additional_field/list.html:9
 msgid "Additional fields"
 msgstr ""
 
-#: aleksis/core/menus.py:315
+#: aleksis/core/menus.py:309
+msgid "Invite person"
+msgstr ""
+
+#: aleksis/core/menus.py:322
 #: aleksis/core/templates/core/group/child_groups.html:7
 #: aleksis/core/templates/core/group/child_groups.html:9
 msgid "Assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/mixins.py:508
+#: aleksis/core/mixins.py:511
 msgid "Linked school term"
 msgstr ""
 
-#: aleksis/core/models.py:66
+#: aleksis/core/models.py:70
 msgid "Boolean (Yes/No)"
 msgstr ""
 
-#: aleksis/core/models.py:67
+#: aleksis/core/models.py:71
 msgid "Text (one line)"
 msgstr ""
 
-#: aleksis/core/models.py:69
+#: aleksis/core/models.py:73
 msgid "Date and time"
 msgstr ""
 
-#: aleksis/core/models.py:70
+#: aleksis/core/models.py:74
 msgid "Decimal number"
 msgstr ""
 
-#: aleksis/core/models.py:71 aleksis/core/models.py:194
+#: aleksis/core/models.py:75 aleksis/core/models.py:198
 msgid "E-mail address"
 msgstr ""
 
-#: aleksis/core/models.py:72
+#: aleksis/core/models.py:76
 msgid "Integer"
 msgstr ""
 
-#: aleksis/core/models.py:73
+#: aleksis/core/models.py:77
 msgid "IP address"
 msgstr ""
 
-#: aleksis/core/models.py:74
+#: aleksis/core/models.py:78
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr ""
 
-#: aleksis/core/models.py:75
+#: aleksis/core/models.py:79
 msgid "Text (multi-line)"
 msgstr ""
 
-#: aleksis/core/models.py:77
+#: aleksis/core/models.py:81
 msgid "URL / Link"
 msgstr ""
 
-#: aleksis/core/models.py:89 aleksis/core/models.py:948
+#: aleksis/core/models.py:93 aleksis/core/models.py:950
 msgid "Name"
 msgstr ""
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:95
 msgid "Start date"
 msgstr ""
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:96
 msgid "End date"
 msgstr ""
 
-#: aleksis/core/models.py:111
+#: aleksis/core/models.py:115
 msgid "The start date must be earlier than the end date."
 msgstr ""
 
-#: aleksis/core/models.py:118
+#: aleksis/core/models.py:122
 msgid "There is already a school term for this time or a part of this time."
 msgstr ""
 
-#: aleksis/core/models.py:147 aleksis/core/models.py:897
+#: aleksis/core/models.py:151 aleksis/core/models.py:899
 msgid "Person"
 msgstr ""
 
-#: aleksis/core/models.py:150
+#: aleksis/core/models.py:154
 msgid "Can view address"
 msgstr ""
 
-#: aleksis/core/models.py:151
+#: aleksis/core/models.py:155
 msgid "Can view contact details"
 msgstr ""
 
-#: aleksis/core/models.py:152
+#: aleksis/core/models.py:156
 msgid "Can view photo"
 msgstr ""
 
-#: aleksis/core/models.py:153
+#: aleksis/core/models.py:157
 msgid "Can view persons groups"
 msgstr ""
 
-#: aleksis/core/models.py:154
+#: aleksis/core/models.py:158
 msgid "Can view personal details"
 msgstr ""
 
-#: aleksis/core/models.py:164
+#: aleksis/core/models.py:168
 msgid "female"
 msgstr ""
 
-#: aleksis/core/models.py:164
+#: aleksis/core/models.py:168
 msgid "male"
 msgstr ""
 
-#: aleksis/core/models.py:172
+#: aleksis/core/models.py:176 aleksis/core/models.py:1144
 msgid "Linked user"
 msgstr ""
 
-#: aleksis/core/models.py:174
+#: aleksis/core/models.py:178
 msgid "Is person active?"
 msgstr ""
 
-#: aleksis/core/models.py:176
+#: aleksis/core/models.py:180
 msgid "First name"
 msgstr ""
 
-#: aleksis/core/models.py:177
+#: aleksis/core/models.py:181
 msgid "Last name"
 msgstr ""
 
-#: aleksis/core/models.py:179
+#: aleksis/core/models.py:183
 msgid "Additional name(s)"
 msgstr ""
 
-#: aleksis/core/models.py:183 aleksis/core/models.py:429
+#: aleksis/core/models.py:187 aleksis/core/models.py:431
 msgid "Short name"
 msgstr ""
 
-#: aleksis/core/models.py:186
+#: aleksis/core/models.py:190
 msgid "Street"
 msgstr ""
 
-#: aleksis/core/models.py:187
+#: aleksis/core/models.py:191
 msgid "Street number"
 msgstr ""
 
-#: aleksis/core/models.py:188
+#: aleksis/core/models.py:192
 msgid "Postal code"
 msgstr ""
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:193
 msgid "Place"
 msgstr ""
 
-#: aleksis/core/models.py:191
+#: aleksis/core/models.py:195
 msgid "Home phone"
 msgstr ""
 
-#: aleksis/core/models.py:192
+#: aleksis/core/models.py:196
 msgid "Mobile phone"
 msgstr ""
 
-#: aleksis/core/models.py:196
+#: aleksis/core/models.py:200
 msgid "Date of birth"
 msgstr ""
 
-#: aleksis/core/models.py:198
+#: aleksis/core/models.py:201
 msgid "Place of birth"
 msgstr ""
 
-#: aleksis/core/models.py:200
+#: aleksis/core/models.py:202
 msgid "Sex"
 msgstr ""
 
-#: aleksis/core/models.py:202
+#: aleksis/core/models.py:204
 msgid "Photo"
 msgstr ""
 
-#: aleksis/core/models.py:206 aleksis/core/templates/core/person/full.html:138
+#: aleksis/core/models.py:208 aleksis/core/templates/core/person/full.html:145
 msgid "Guardians / Parents"
 msgstr ""
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:215
 msgid "Primary group"
 msgstr ""
 
-#: aleksis/core/models.py:216 aleksis/core/models.py:583
-#: aleksis/core/models.py:607 aleksis/core/models.py:692
-#: aleksis/core/models.py:972 aleksis/core/templates/core/person/full.html:121
+#: aleksis/core/models.py:218 aleksis/core/models.py:585
+#: aleksis/core/models.py:609 aleksis/core/models.py:694
+#: aleksis/core/models.py:974 aleksis/core/templates/core/person/full.html:128
 msgid "Description"
 msgstr ""
 
-#: aleksis/core/models.py:384
+#: aleksis/core/models.py:386
 msgid "Title of field"
 msgstr ""
 
-#: aleksis/core/models.py:386
+#: aleksis/core/models.py:388
 msgid "Type of field"
 msgstr ""
 
-#: aleksis/core/models.py:393
+#: aleksis/core/models.py:395
 msgid "Addtitional field for groups"
 msgstr ""
 
-#: aleksis/core/models.py:394
+#: aleksis/core/models.py:396
 msgid "Addtitional fields for groups"
 msgstr ""
 
-#: aleksis/core/models.py:414
+#: aleksis/core/models.py:416
 msgid "Can assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/models.py:415
+#: aleksis/core/models.py:417
 msgid "Can view statistics about group."
 msgstr ""
 
-#: aleksis/core/models.py:427
+#: aleksis/core/models.py:429
 msgid "Long name"
 msgstr ""
 
-#: aleksis/core/models.py:437 aleksis/core/templates/core/group/full.html:85
+#: aleksis/core/models.py:439 aleksis/core/templates/core/group/full.html:85
 msgid "Members"
 msgstr ""
 
-#: aleksis/core/models.py:440 aleksis/core/templates/core/group/full.html:82
+#: aleksis/core/models.py:442 aleksis/core/templates/core/group/full.html:82
 msgid "Owners"
 msgstr ""
 
-#: aleksis/core/models.py:447 aleksis/core/templates/core/group/full.html:55
+#: aleksis/core/models.py:449 aleksis/core/templates/core/group/full.html:55
 msgid "Parent groups"
 msgstr ""
 
-#: aleksis/core/models.py:455
+#: aleksis/core/models.py:457
 msgid "Type of group"
 msgstr ""
 
-#: aleksis/core/models.py:582 aleksis/core/models.py:606
-#: aleksis/core/models.py:691
+#: aleksis/core/models.py:584 aleksis/core/models.py:608
+#: aleksis/core/models.py:693
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr ""
 
-#: aleksis/core/models.py:585
+#: aleksis/core/models.py:587
 msgid "Application"
 msgstr ""
 
-#: aleksis/core/models.py:591
+#: aleksis/core/models.py:593
 msgid "Activity"
 msgstr ""
 
-#: aleksis/core/models.py:592
+#: aleksis/core/models.py:594
 msgid "Activities"
 msgstr ""
 
-#: aleksis/core/models.py:598
+#: aleksis/core/models.py:600
 msgid "Sender"
 msgstr ""
 
-#: aleksis/core/models.py:603
+#: aleksis/core/models.py:605
 msgid "Recipient"
 msgstr ""
 
-#: aleksis/core/models.py:608 aleksis/core/models.py:949
+#: aleksis/core/models.py:610 aleksis/core/models.py:951
 msgid "Link"
 msgstr ""
 
-#: aleksis/core/models.py:610
+#: aleksis/core/models.py:612
 msgid "Read"
 msgstr ""
 
-#: aleksis/core/models.py:611
+#: aleksis/core/models.py:613
 msgid "Sent"
 msgstr ""
 
-#: aleksis/core/models.py:624
+#: aleksis/core/models.py:626
 msgid "Notification"
 msgstr ""
 
-#: aleksis/core/models.py:693
+#: aleksis/core/models.py:695
 msgid "Link to detailed view"
 msgstr ""
 
-#: aleksis/core/models.py:696
+#: aleksis/core/models.py:698
 msgid "Date and time from when to show"
 msgstr ""
 
-#: aleksis/core/models.py:699
+#: aleksis/core/models.py:701
 msgid "Date and time until when to show"
 msgstr ""
 
-#: aleksis/core/models.py:724
+#: aleksis/core/models.py:726
 msgid "Announcement"
 msgstr ""
 
-#: aleksis/core/models.py:762
+#: aleksis/core/models.py:764
 msgid "Announcement recipient"
 msgstr ""
 
-#: aleksis/core/models.py:763
+#: aleksis/core/models.py:765
 msgid "Announcement recipients"
 msgstr ""
 
-#: aleksis/core/models.py:818
+#: aleksis/core/models.py:820
 msgid "Widget Title"
 msgstr ""
 
-#: aleksis/core/models.py:819
+#: aleksis/core/models.py:821
 msgid "Activate Widget"
 msgstr ""
 
-#: aleksis/core/models.py:820
+#: aleksis/core/models.py:822
 msgid "Widget is broken"
 msgstr ""
 
-#: aleksis/core/models.py:823
+#: aleksis/core/models.py:825
 msgid "Size on mobile devices"
 msgstr ""
 
-#: aleksis/core/models.py:824
+#: aleksis/core/models.py:826
 msgid "<= 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:829
+#: aleksis/core/models.py:831
 msgid "Size on tablet devices"
 msgstr ""
 
-#: aleksis/core/models.py:830
+#: aleksis/core/models.py:832
 msgid "> 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:835
+#: aleksis/core/models.py:837
 msgid "Size on desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:836
+#: aleksis/core/models.py:838
 msgid "> 992 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:841
+#: aleksis/core/models.py:843
 msgid "Size on large desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:842
+#: aleksis/core/models.py:844
 msgid "> 1200 px>, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:873
+#: aleksis/core/models.py:875
 msgid "Can edit default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:874
+#: aleksis/core/models.py:876
 msgid "Dashboard Widget"
 msgstr ""
 
-#: aleksis/core/models.py:875
+#: aleksis/core/models.py:877
 msgid "Dashboard Widgets"
 msgstr ""
 
-#: aleksis/core/models.py:881
+#: aleksis/core/models.py:883
 msgid "URL"
 msgstr ""
 
-#: aleksis/core/models.py:882
+#: aleksis/core/models.py:884
 msgid "Icon URL"
 msgstr ""
 
-#: aleksis/core/models.py:888
+#: aleksis/core/models.py:890
 msgid "External link widget"
 msgstr ""
 
-#: aleksis/core/models.py:889
+#: aleksis/core/models.py:891
 msgid "External link widgets"
 msgstr ""
 
-#: aleksis/core/models.py:894
+#: aleksis/core/models.py:896
 msgid "Dashboard widget"
 msgstr ""
 
-#: aleksis/core/models.py:899
+#: aleksis/core/models.py:901
 msgid "Order"
 msgstr ""
 
-#: aleksis/core/models.py:900
+#: aleksis/core/models.py:902
 msgid "Part of the default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:915
+#: aleksis/core/models.py:917
 msgid "Dashboard widget order"
 msgstr ""
 
-#: aleksis/core/models.py:916
+#: aleksis/core/models.py:918
 msgid "Dashboard widget orders"
 msgstr ""
 
-#: aleksis/core/models.py:922
+#: aleksis/core/models.py:924
 msgid "Menu ID"
 msgstr ""
 
-#: aleksis/core/models.py:935
+#: aleksis/core/models.py:937
 msgid "Custom menu"
 msgstr ""
 
-#: aleksis/core/models.py:936
+#: aleksis/core/models.py:938
 msgid "Custom menus"
 msgstr ""
 
-#: aleksis/core/models.py:946
+#: aleksis/core/models.py:948
 msgid "Menu"
 msgstr ""
 
-#: aleksis/core/models.py:950
+#: aleksis/core/models.py:952
 msgid "Icon"
 msgstr ""
 
-#: aleksis/core/models.py:956
+#: aleksis/core/models.py:958
 msgid "Custom menu item"
 msgstr ""
 
-#: aleksis/core/models.py:957
+#: aleksis/core/models.py:959
 msgid "Custom menu items"
 msgstr ""
 
-#: aleksis/core/models.py:971
+#: aleksis/core/models.py:973
 msgid "Title of type"
 msgstr ""
 
-#: aleksis/core/models.py:978 aleksis/core/templates/core/group/full.html:47
+#: aleksis/core/models.py:980 aleksis/core/templates/core/group/full.html:47
 msgid "Group type"
 msgstr ""
 
-#: aleksis/core/models.py:992
+#: aleksis/core/models.py:994
 msgid "Can view system status"
 msgstr ""
 
-#: aleksis/core/models.py:993
+#: aleksis/core/models.py:995
 msgid "Can manage data"
 msgstr ""
 
-#: aleksis/core/models.py:994
+#: aleksis/core/models.py:996
 msgid "Can impersonate"
 msgstr ""
 
-#: aleksis/core/models.py:995
+#: aleksis/core/models.py:997
 msgid "Can use search"
 msgstr ""
 
-#: aleksis/core/models.py:996
+#: aleksis/core/models.py:998
 msgid "Can change site preferences"
 msgstr ""
 
-#: aleksis/core/models.py:997
+#: aleksis/core/models.py:999
 msgid "Can change person preferences"
 msgstr ""
 
-#: aleksis/core/models.py:998
+#: aleksis/core/models.py:1000
 msgid "Can change group preferences"
 msgstr ""
 
-#: aleksis/core/models.py:999
+#: aleksis/core/models.py:1001
 msgid "Can test PDF generation"
 msgstr ""
 
-#: aleksis/core/models.py:1035
+#: aleksis/core/models.py:1037
 msgid "Related data check task"
 msgstr ""
 
-#: aleksis/core/models.py:1043
+#: aleksis/core/models.py:1045
 msgid "Issue solved"
 msgstr ""
 
-#: aleksis/core/models.py:1044
+#: aleksis/core/models.py:1046
 msgid "Notification sent"
 msgstr ""
 
-#: aleksis/core/models.py:1057
+#: aleksis/core/models.py:1059
 msgid "Data check result"
 msgstr ""
 
-#: aleksis/core/models.py:1058
+#: aleksis/core/models.py:1060
 msgid "Data check results"
 msgstr ""
 
-#: aleksis/core/models.py:1060
+#: aleksis/core/models.py:1062
 msgid "Can run data checks"
 msgstr ""
 
-#: aleksis/core/models.py:1061
+#: aleksis/core/models.py:1063
 msgid "Can solve data check problems"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1070
+msgid "E-Mail address"
+msgstr ""
+
+#: aleksis/core/models.py:1094
 msgid "Owner"
 msgstr ""
 
-#: aleksis/core/models.py:1080
+#: aleksis/core/models.py:1098
 msgid "File expires at"
 msgstr ""
 
-#: aleksis/core/models.py:1082
+#: aleksis/core/models.py:1100
 msgid "Generated HTML file"
 msgstr ""
 
-#: aleksis/core/models.py:1084
+#: aleksis/core/models.py:1102
 msgid "Generated PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1091
+#: aleksis/core/models.py:1109
 msgid "PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1092
+#: aleksis/core/models.py:1110
 msgid "PDF files"
 msgstr ""
 
-#: aleksis/core/models.py:1097
+#: aleksis/core/models.py:1115
 msgid "Task result"
 msgstr ""
 
-#: aleksis/core/models.py:1100
+#: aleksis/core/models.py:1118
 msgid "Task user"
 msgstr ""
 
-#: aleksis/core/models.py:1112
+#: aleksis/core/models.py:1130
 msgid "Task user assignment"
 msgstr ""
 
-#: aleksis/core/models.py:1113
+#: aleksis/core/models.py:1131
 msgid "Task user assignments"
 msgstr ""
 
-#: aleksis/core/models.py:1127
+#: aleksis/core/models.py:1147
+msgid "Additional attributes"
+msgstr ""
+
+#: aleksis/core/models.py:1185
 msgid "Allowed scopes that clients can request"
 msgstr ""
 
-#: aleksis/core/preferences.py:23
+#: aleksis/core/preferences.py:24
 msgid "General"
 msgstr ""
 
-#: aleksis/core/preferences.py:24
+#: aleksis/core/preferences.py:25
 msgid "School"
 msgstr ""
 
-#: aleksis/core/preferences.py:25
+#: aleksis/core/preferences.py:26
 msgid "Theme"
 msgstr ""
 
-#: aleksis/core/preferences.py:26
+#: aleksis/core/preferences.py:27
 msgid "Mail"
 msgstr ""
 
-#: aleksis/core/preferences.py:28
+#: aleksis/core/preferences.py:29
 msgid "Footer"
 msgstr ""
 
-#: aleksis/core/preferences.py:29
+#: aleksis/core/preferences.py:30
 msgid "Accounts"
 msgstr ""
 
-#: aleksis/core/preferences.py:30
+#: aleksis/core/preferences.py:31
 msgid "Authentication"
 msgstr ""
 
-#: aleksis/core/preferences.py:31
+#: aleksis/core/preferences.py:32
 msgid "Internationalisation"
 msgstr ""
 
-#: aleksis/core/preferences.py:42
+#: aleksis/core/preferences.py:43
 msgid "Site title"
 msgstr ""
 
-#: aleksis/core/preferences.py:53
+#: aleksis/core/preferences.py:54
 msgid "Site description"
 msgstr ""
 
-#: aleksis/core/preferences.py:64
+#: aleksis/core/preferences.py:65
 msgid "Primary colour"
 msgstr ""
 
-#: aleksis/core/preferences.py:76
+#: aleksis/core/preferences.py:77
 msgid "Secondary colour"
 msgstr ""
 
-#: aleksis/core/preferences.py:87
+#: aleksis/core/preferences.py:88
 msgid "Logo"
 msgstr ""
 
-#: aleksis/core/preferences.py:97
+#: aleksis/core/preferences.py:98
 msgid "Favicon"
 msgstr ""
 
-#: aleksis/core/preferences.py:107
+#: aleksis/core/preferences.py:108
 msgid "PWA-Icon"
 msgstr ""
 
-#: aleksis/core/preferences.py:118
+#: aleksis/core/preferences.py:119
 msgid "Mail out name"
 msgstr ""
 
-#: aleksis/core/preferences.py:129
+#: aleksis/core/preferences.py:130
 msgid "Mail out address"
 msgstr ""
 
-#: aleksis/core/preferences.py:141
+#: aleksis/core/preferences.py:142
 msgid "Link to privacy policy"
 msgstr ""
 
-#: aleksis/core/preferences.py:153
+#: aleksis/core/preferences.py:154
 msgid "Link to imprint"
 msgstr ""
 
-#: aleksis/core/preferences.py:165
+#: aleksis/core/preferences.py:166
 msgid "Name format for addressing"
 msgstr ""
 
-#: aleksis/core/preferences.py:181
+#: aleksis/core/preferences.py:182
 msgid "Channels to use for notifications"
 msgstr ""
 
-#: aleksis/core/preferences.py:193
+#: aleksis/core/preferences.py:194
 msgid "Regular expression to match primary group, e.g. '^Class .*'"
 msgstr ""
 
-#: aleksis/core/preferences.py:204
+#: aleksis/core/preferences.py:205
 msgid "Field on person to match primary group against"
 msgstr ""
 
-#: aleksis/core/preferences.py:216
+#: aleksis/core/preferences.py:217
 msgid "Automatically create new persons for new users"
 msgstr ""
 
-#: aleksis/core/preferences.py:225
+#: aleksis/core/preferences.py:226
 msgid "Automatically link existing persons to new users by their e-mail address"
 msgstr ""
 
-#: aleksis/core/preferences.py:236
+#: aleksis/core/preferences.py:237
 msgid "Display name of the school"
 msgstr ""
 
-#: aleksis/core/preferences.py:247
+#: aleksis/core/preferences.py:248
 msgid "Official name of the school, e.g. as given by supervisory authority"
 msgstr ""
 
-#: aleksis/core/preferences.py:255
+#: aleksis/core/preferences.py:256
 msgid "Allow users to change their passwords"
 msgstr ""
 
-#: aleksis/core/preferences.py:263
+#: aleksis/core/preferences.py:264
 msgid "Enable signup"
 msgstr ""
 
-#: aleksis/core/preferences.py:274
+#: aleksis/core/preferences.py:272
+msgid "Enable invitations"
+msgstr ""
+
+#: aleksis/core/preferences.py:280
+msgid "Length of invite code. (Default 3: abcde-acbde-abcde)"
+msgstr ""
+
+#: aleksis/core/preferences.py:288
+msgid "Size of packets. (Default 5: abcde)"
+msgstr ""
+
+#: aleksis/core/preferences.py:298
 msgid "Allowed Grant Flows for OAuth applications"
 msgstr ""
 
-#: aleksis/core/preferences.py:287
+#: aleksis/core/preferences.py:311
 msgid "Available languages"
 msgstr ""
 
-#: aleksis/core/preferences.py:299
+#: aleksis/core/preferences.py:323
 msgid "Send emails if data checks detect problems"
 msgstr ""
 
-#: aleksis/core/preferences.py:310
+#: aleksis/core/preferences.py:334
 msgid "Email recipients for data checks problem emails"
 msgstr ""
 
-#: aleksis/core/preferences.py:321
+#: aleksis/core/preferences.py:345
 msgid "Email recipient groups for data checks problem emails"
 msgstr ""
 
-#: aleksis/core/preferences.py:330
+#: aleksis/core/preferences.py:354
 msgid "Show dashboard to users without login"
 msgstr ""
 
-#: aleksis/core/preferences.py:339
+#: aleksis/core/preferences.py:363
 msgid "Allow users to edit their dashboard"
 msgstr ""
 
-#: aleksis/core/preferences.py:350
+#: aleksis/core/preferences.py:374
 msgid "Fields on person model which are editable by themselves."
 msgstr ""
 
-#: aleksis/core/preferences.py:364
+#: aleksis/core/preferences.py:388
 msgid "Editable fields on person model which should trigger a notification on change"
 msgstr ""
 
-#: aleksis/core/preferences.py:377
+#: aleksis/core/preferences.py:401
 msgid "Contact for notification if a person changes their data"
 msgstr ""
 
-#: aleksis/core/preferences.py:387
+#: aleksis/core/preferences.py:411
 msgid "PDF file expiration duration"
 msgstr ""
 
-#: aleksis/core/preferences.py:388
+#: aleksis/core/preferences.py:412
 msgid "in minutes"
 msgstr ""
 
-#: aleksis/core/preferences.py:398
+#: aleksis/core/preferences.py:422
 msgid "Automatically update the dashboard and its widgets"
 msgstr ""
 
-#: aleksis/core/preferences.py:408
+#: aleksis/core/preferences.py:432
 msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr ""
 
-#: aleksis/core/settings.py:474
+#: aleksis/core/settings.py:507
 msgid "English"
 msgstr ""
 
-#: aleksis/core/settings.py:475
+#: aleksis/core/settings.py:508
 msgid "German"
 msgstr ""
 
-#: aleksis/core/tables.py:19
+#: aleksis/core/tables.py:24
 #: aleksis/core/templates/core/announcement/list.html:36
 #: aleksis/core/templates/core/group/full.html:24
-#: aleksis/core/templates/core/person/full.html:23
+#: aleksis/core/templates/core/person/full.html:24
 #: aleksis/core/templates/oauth2_provider/application/detail.html:17
 msgid "Edit"
 msgstr ""
 
-#: aleksis/core/tables.py:21 aleksis/core/tables.py:89
-#: aleksis/core/tables.py:105
+#: aleksis/core/tables.py:26 aleksis/core/tables.py:94
+#: aleksis/core/tables.py:137
 #: aleksis/core/templates/core/announcement/list.html:22
 msgid "Actions"
 msgstr ""
 
-#: aleksis/core/tables.py:56 aleksis/core/tables.py:57
-#: aleksis/core/tables.py:71 aleksis/core/tables.py:87
-#: aleksis/core/tables.py:103
+#: aleksis/core/tables.py:61 aleksis/core/tables.py:62
+#: aleksis/core/tables.py:76 aleksis/core/tables.py:92
+#: aleksis/core/tables.py:135
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/group/full.html:31
 #: aleksis/core/templates/core/pages/delete.html:22
-#: aleksis/core/templates/core/person/full.html:30
+#: aleksis/core/templates/core/person/full.html:31
 #: aleksis/core/templates/oauth2_provider/application/detail.html:21
 msgid "Delete"
 msgstr ""
@@ -1208,11 +1227,11 @@ msgstr ""
 msgid "Account inactive"
 msgstr ""
 
-#: aleksis/core/templates/account/account_inactive.html:13
+#: aleksis/core/templates/account/account_inactive.html:14
 msgid "Account inactive."
 msgstr ""
 
-#: aleksis/core/templates/account/account_inactive.html:15
+#: aleksis/core/templates/account/account_inactive.html:17
 msgid ""
 "\n"
 "            This account is currently inactive. If you think this is an\n"
@@ -1364,11 +1383,11 @@ msgstr ""
 msgid "Signup closed"
 msgstr ""
 
-#: aleksis/core/templates/account/signup_closed.html:13
+#: aleksis/core/templates/account/signup_closed.html:14
 msgid "Signup closed."
 msgstr ""
 
-#: aleksis/core/templates/account/signup_closed.html:15
+#: aleksis/core/templates/account/signup_closed.html:17
 msgid ""
 "\n"
 "            This sign up is currently closed. If you think this is an\n"
@@ -1406,11 +1425,6 @@ msgid ""
 "          "
 msgstr ""
 
-#: aleksis/core/templates/account/verification_sent.html:30
-#, python-format
-msgid "<strong>Note:</strong> you can still <a href=\"%(email_url)s\">change your e-mail address</a>"
-msgstr ""
-
 #: aleksis/core/templates/core/additional_field/edit.html:6
 #: aleksis/core/templates/core/additional_field/edit.html:7
 msgid "Edit additional field"
@@ -1459,11 +1473,11 @@ msgid "Logged in as"
 msgstr ""
 
 #: aleksis/core/templates/core/base.html:175
-msgid "About AlekSIS — The Free School Information System"
+msgid "About AlekSIS® — The Free School Information System"
 msgstr ""
 
 #: aleksis/core/templates/core/base.html:183
-msgid "Impress"
+msgid "Imprint"
 msgstr ""
 
 #: aleksis/core/templates/core/base.html:191
@@ -1471,7 +1485,7 @@ msgid "Privacy Policy"
 msgstr ""
 
 #: aleksis/core/templates/core/base_print.html:72
-msgid "Powered by AlekSIS"
+msgid "Powered by AlekSIS®"
 msgstr ""
 
 #: aleksis/core/templates/core/dashboard_widget/create.html:8
@@ -1686,7 +1700,7 @@ msgid "Edit group"
 msgstr ""
 
 #: aleksis/core/templates/core/group/full.html:38
-#: aleksis/core/templates/core/person/full.html:37
+#: aleksis/core/templates/core/person/full.html:38
 msgid "Change preferences"
 msgstr ""
 
@@ -1775,36 +1789,46 @@ msgid "No notifications available yet."
 msgstr ""
 
 #: aleksis/core/templates/core/pages/about.html:6
-#: aleksis/core/templates/core/pages/about.html:15
-msgid "About AlekSIS"
+msgid "About AlekSIS®"
 msgstr ""
 
 #: aleksis/core/templates/core/pages/about.html:7
-msgid "AlekSIS – The Free School Information System"
+msgid "AlekSIS® – The Free School Information System"
+msgstr ""
+
+#: aleksis/core/templates/core/pages/about.html:15
+msgid "About AlekSIS"
 msgstr ""
 
 #: aleksis/core/templates/core/pages/about.html:17
 msgid ""
 "\n"
-"              This platform is powered by AlekSIS, a web-based school information system (SIS) which can be used\n"
+"              This platform is powered by AlekSIS®, a web-based school information system (SIS) which can be used\n"
 "              to manage and/or publish organisational artifacts of educational institutions. AlekSIS is free software and\n"
 "              can be used by anyone.\n"
 "            "
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:25
+#: aleksis/core/templates/core/pages/about.html:24
+msgid ""
+"\n"
+"              AlekSIS® is a registered trademark of the AlekSIS open source project, represented by Teckids e.V.\n"
+"            "
+msgstr ""
+
+#: aleksis/core/templates/core/pages/about.html:30
 msgid "Website of AlekSIS"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:26
+#: aleksis/core/templates/core/pages/about.html:31
 msgid "Source code"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:35
+#: aleksis/core/templates/core/pages/about.html:40
 msgid "Licence information"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:37
+#: aleksis/core/templates/core/pages/about.html:42
 msgid ""
 "\n"
 "              The core and the official apps of AlekSIS are licenced under the EUPL, version 1.2 or later. For licence\n"
@@ -1813,23 +1837,23 @@ msgid ""
 "            "
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:45
+#: aleksis/core/templates/core/pages/about.html:50
 msgid "Free/Open Source Licence"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:46
+#: aleksis/core/templates/core/pages/about.html:51
 msgid "Other Licence"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:50
+#: aleksis/core/templates/core/pages/about.html:55
 msgid "Full licence text"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:51
+#: aleksis/core/templates/core/pages/about.html:56
 msgid "More information about the EUPL"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:90
+#: aleksis/core/templates/core/pages/about.html:95
 #, python-format
 msgid ""
 "\n"
@@ -2070,17 +2094,19 @@ msgstr ""
 msgid "Edit person"
 msgstr ""
 
-#: aleksis/core/templates/core/person/full.html:44
-#: aleksis/core/templates/impersonate/list_users.html:7
-#: aleksis/core/templates/impersonate/list_users.html:8
+#: aleksis/core/templates/core/person/full.html:45
 msgid "Impersonate"
 msgstr ""
 
-#: aleksis/core/templates/core/person/full.html:50
+#: aleksis/core/templates/core/person/full.html:51
+msgid "Invite user"
+msgstr ""
+
+#: aleksis/core/templates/core/person/full.html:57
 msgid "Contact details"
 msgstr ""
 
-#: aleksis/core/templates/core/person/full.html:131
+#: aleksis/core/templates/core/person/full.html:138
 msgid "Children"
 msgstr ""
 
@@ -2120,6 +2146,49 @@ msgstr ""
 msgid "Save preferences"
 msgstr ""
 
+#: aleksis/core/templates/invitations/enter.html:21
+msgid "Accept your invitation"
+msgstr ""
+
+#: aleksis/core/templates/invitations/enter.html:25
+msgid ""
+"\n"
+"                Please enter your invitation code to register\n"
+"                your new user account:\n"
+"              "
+msgstr ""
+
+#: aleksis/core/templates/invitations/enter.html:37
+msgid "Accept invite"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:9
+#: aleksis/core/templates/invitations/forms/_invite.html:10
+#: aleksis/core/templates/invitations/forms/_invite.html:21
+msgid "Invite"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:17
+msgid "Invite by email address"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:26
+msgid "Generate invitation code"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:29
+msgid "Generate code"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:33
+msgid "Invitations"
+msgstr ""
+
+#: aleksis/core/templates/invitations/messages/invite_accepted.txt:3
+#, python-format
+msgid "The invitation for %(email)s has been accepted."
+msgstr ""
+
 #: aleksis/core/templates/oauth2_provider/application/create.html:5
 #: aleksis/core/templates/oauth2_provider/application/create.html:6
 msgid "Register OAuth2 Application"
@@ -2780,147 +2849,160 @@ msgstr ""
 msgid "SMS"
 msgstr ""
 
-#: aleksis/core/util/pdf.py:105
+#: aleksis/core/util/pdf.py:113
 msgid "Progress: Generate PDF file"
 msgstr ""
 
-#: aleksis/core/util/pdf.py:106
+#: aleksis/core/util/pdf.py:114
 msgid "Generating PDF file …"
 msgstr ""
 
-#: aleksis/core/util/pdf.py:107
+#: aleksis/core/util/pdf.py:115
 msgid "The PDF file has been generated successfully."
 msgstr ""
 
-#: aleksis/core/util/pdf.py:108
+#: aleksis/core/util/pdf.py:116
 msgid "There was a problem while generating the PDF file."
 msgstr ""
 
-#: aleksis/core/util/pdf.py:111
+#: aleksis/core/util/pdf.py:119
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/core/views.py:270
+#: aleksis/core/views.py:280
 msgid "The school term has been created."
 msgstr ""
 
-#: aleksis/core/views.py:282
+#: aleksis/core/views.py:292
 msgid "The school term has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:406
+#: aleksis/core/views.py:416
 msgid "The child groups were successfully saved."
 msgstr ""
 
-#: aleksis/core/views.py:425 aleksis/core/views.py:435
+#: aleksis/core/views.py:435 aleksis/core/views.py:445
 msgid "The person has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:485
+#: aleksis/core/views.py:495
 msgid "The group has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:582
+#: aleksis/core/views.py:592
 msgid "The announcement has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:598
+#: aleksis/core/views.py:608
 msgid "The announcement has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:682
+#: aleksis/core/views.py:695
 msgid "The preferences have been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:706
+#: aleksis/core/views.py:719
 msgid "The person has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:720
+#: aleksis/core/views.py:733
 msgid "The group has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:752
+#: aleksis/core/views.py:765
 msgid "The additional_field has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:786
+#: aleksis/core/views.py:799
 msgid "The additional field has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:811
+#: aleksis/core/views.py:824
 msgid "The group type has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:841
+#: aleksis/core/views.py:854
 msgid "The group type has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:874
+#: aleksis/core/views.py:887
 msgid "Progress: Run data checks"
 msgstr ""
 
-#: aleksis/core/views.py:875
+#: aleksis/core/views.py:888
 msgid "Run data checks …"
 msgstr ""
 
-#: aleksis/core/views.py:876
+#: aleksis/core/views.py:889
 msgid "The data checks were run successfully."
 msgstr ""
 
-#: aleksis/core/views.py:877
+#: aleksis/core/views.py:890
 msgid "There was a problem while running data checks."
 msgstr ""
 
-#: aleksis/core/views.py:893
+#: aleksis/core/views.py:906
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr ""
 
-#: aleksis/core/views.py:935
+#: aleksis/core/views.py:948
 msgid "The dashboard widget has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:965
+#: aleksis/core/views.py:978
 msgid "The dashboard widget has been created."
 msgstr ""
 
-#: aleksis/core/views.py:975
+#: aleksis/core/views.py:988
 msgid "The dashboard widget has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1042
+#: aleksis/core/views.py:1055
 msgid "Your dashboard configuration has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1044
+#: aleksis/core/views.py:1057
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1139
+#: aleksis/core/views.py:1127
+#, python-brace-format
+msgid "The invitation was successfully created. The invitation code is {code}"
+msgstr ""
+
+#: aleksis/core/views.py:1218
 msgid "We have successfully assigned the permissions."
 msgstr ""
 
-#: aleksis/core/views.py:1149
+#: aleksis/core/views.py:1228
 msgid "The global user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1159
+#: aleksis/core/views.py:1238
 msgid "The global group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1169
+#: aleksis/core/views.py:1248
 msgid "The object user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1179
+#: aleksis/core/views.py:1258
 msgid "The object group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1298
+#: aleksis/core/views.py:1377
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr ""
 
-#: aleksis/core/views.py:1305
+#: aleksis/core/views.py:1384
 msgid "The third-party account has been successfully disconnected."
 msgstr ""
+
+#: aleksis/core/views.py:1441
+msgid "Person was invited successfully."
+msgstr ""
+
+#: aleksis/core/views.py:1443
+msgid "Person was already invited."
+msgstr ""
diff --git a/aleksis/core/locale/ar/LC_MESSAGES/djangojs.po b/aleksis/core/locale/ar/LC_MESSAGES/djangojs.po
index eacffd3e211761afbf5bb697e91ee630c6d3eabc..ac0e765ef8e4c2d7ac490397fcd99e22fd921846 100644
--- a/aleksis/core/locale/ar/LC_MESSAGES/djangojs.po
+++ b/aleksis/core/locale/ar/LC_MESSAGES/djangojs.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-11-29 09:59+0100\n"
+"POT-Creation-Date: 2021-12-28 12:14+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -30,6 +30,6 @@ msgstr ""
 msgid "OK"
 msgstr ""
 
-#: aleksis/core/static/js/main.js:127
+#: aleksis/core/static/js/main.js:128
 msgid "This page may contain outdated information since there is no internet connection."
 msgstr ""
diff --git a/aleksis/core/locale/de_DE/LC_MESSAGES/django.po b/aleksis/core/locale/de_DE/LC_MESSAGES/django.po
index a4078edbe8ad41e7ed85dc18d522f063c7cce33c..6a7afee49da25f0111b97b04ca8f4f51eb7aeba8 100644
--- a/aleksis/core/locale/de_DE/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/de_DE/LC_MESSAGES/django.po
@@ -7,8 +7,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: AlekSIS (School Information System) 0.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-11-29 09:59+0100\n"
-"PO-Revision-Date: 2021-11-29 09:34+0000\n"
+"POT-Creation-Date: 2021-12-28 12:13+0100\n"
+"PO-Revision-Date: 2021-12-29 13:20+0000\n"
 "Last-Translator: Jonathan Weth <teckids@jonathanweth.de>\n"
 "Language-Team: German <https://translate.edugit.org/projects/aleksis/"
 "aleksis-core/de/>\n"
@@ -19,26 +19,33 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
 "X-Generator: Weblate 4.8\n"
 
-#: aleksis/core/apps.py:151
+#: aleksis/core/apps.py:152
 msgid "OpenID Connect scope"
 msgstr "OpenID-Connect-Scope"
 
-#: aleksis/core/apps.py:152
+#: aleksis/core/apps.py:153
 msgid "Given name, family name, link to profile and picture if existing."
 msgstr "Vorname, Nachname, Link zum Profil und Bild falls vorhanden"
 
-#: aleksis/core/apps.py:153
+#: aleksis/core/apps.py:154
 msgid "Full home postal address"
 msgstr "Vollständige Postanschrift"
 
-#: aleksis/core/apps.py:154
+#: aleksis/core/apps.py:155
 msgid "Email address"
 msgstr "E-Mail-Adresse"
 
-#: aleksis/core/apps.py:155
+#: aleksis/core/apps.py:156
 msgid "Home and mobile phone"
 msgstr "Festnetz- und Mobilfunknummer"
 
+#: aleksis/core/apps.py:157 aleksis/core/forms.py:223 aleksis/core/menus.py:265
+#: aleksis/core/models.py:414 aleksis/core/templates/core/group/list.html:8
+#: aleksis/core/templates/core/group/list.html:9
+#: aleksis/core/templates/core/person/full.html:152
+msgid "Groups"
+msgstr "Gruppen"
+
 #: aleksis/core/data_checks.py:55
 msgid "Ignore problem"
 msgstr "Problem ignorieren"
@@ -85,161 +92,148 @@ msgstr "Berechtigung"
 msgid "Content type"
 msgstr "Inhaltstyp"
 
-#: aleksis/core/filters.py:112 aleksis/core/models.py:579
+#: aleksis/core/filters.py:112 aleksis/core/models.py:581
 msgid "User"
 msgstr "Benutzer"
 
-#: aleksis/core/filters.py:134 aleksis/core/models.py:411
+#: aleksis/core/filters.py:134 aleksis/core/models.py:413
 msgid "Group"
 msgstr "Gruppe"
 
-#: aleksis/core/forms.py:46 aleksis/core/forms.py:514
+#: aleksis/core/forms.py:48 aleksis/core/forms.py:559
 msgid "Base data"
 msgstr "Basisdaten"
 
-#: aleksis/core/forms.py:52
+#: aleksis/core/forms.py:54
 msgid "Address"
 msgstr "Adresse"
 
-#: aleksis/core/forms.py:53
+#: aleksis/core/forms.py:55 aleksis/core/forms.py:568
 msgid "Contact data"
 msgstr "Kontaktdaten"
 
-#: aleksis/core/forms.py:55
+#: aleksis/core/forms.py:57
 msgid "Advanced personal data"
 msgstr "Zusätzliche persönliche Daten"
 
-#: aleksis/core/forms.py:103
+#: aleksis/core/forms.py:105
 msgid "New user"
 msgstr "Neuer Benutzer"
 
-#: aleksis/core/forms.py:103
+#: aleksis/core/forms.py:105
 msgid "Create a new account"
 msgstr "Neues Benutzerkonto erstellen"
 
-#: aleksis/core/forms.py:134
+#: aleksis/core/forms.py:136
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr "Sie können keine neuen Benutzer erstellen, wenn Sie gleichzeitig einen existierenden Benutzer auswählen."
 
-#: aleksis/core/forms.py:138
+#: aleksis/core/forms.py:140
 msgid "This username is already in use."
 msgstr "Dieser Benutzername wird bereits genutzt."
 
-#: aleksis/core/forms.py:155 aleksis/core/models.py:125
+#: aleksis/core/forms.py:157 aleksis/core/models.py:129
 msgid "School term"
 msgstr "Schuljahr"
 
-#: aleksis/core/forms.py:156
+#: aleksis/core/forms.py:158
 msgid "Common data"
 msgstr "Allgemeine Daten"
 
-#: aleksis/core/forms.py:157 aleksis/core/forms.py:208
-#: aleksis/core/menus.py:256 aleksis/core/models.py:148
+#: aleksis/core/forms.py:159 aleksis/core/forms.py:210
+#: aleksis/core/menus.py:254 aleksis/core/models.py:152
 #: aleksis/core/templates/core/person/list.html:8
 #: aleksis/core/templates/core/person/list.html:9
 msgid "Persons"
 msgstr "Personen"
 
-#: aleksis/core/forms.py:158
+#: aleksis/core/forms.py:160 aleksis/core/forms.py:570
 msgid "Additional data"
 msgstr "Zusätzliche Datne"
 
-#: aleksis/core/forms.py:200 aleksis/core/forms.py:203
-#: aleksis/core/models.py:68
+#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
+#: aleksis/core/models.py:72
 msgid "Date"
 msgstr "Datum"
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:76
+#: aleksis/core/forms.py:203 aleksis/core/forms.py:206
+#: aleksis/core/models.py:80
 msgid "Time"
 msgstr "Zeit"
 
-#: aleksis/core/forms.py:221 aleksis/core/menus.py:267
-#: aleksis/core/models.py:412 aleksis/core/templates/core/group/list.html:8
-#: aleksis/core/templates/core/group/list.html:9
-#: aleksis/core/templates/core/person/full.html:145
-msgid "Groups"
-msgstr "Gruppen"
-
-#: aleksis/core/forms.py:234
+#: aleksis/core/forms.py:236
 msgid "From when until when should the announcement be displayed?"
 msgstr "Von wann bis wann soll die Ankündigung angezeigt werden?"
 
-#: aleksis/core/forms.py:237
+#: aleksis/core/forms.py:239
 msgid "Who should see the announcement?"
 msgstr "Wer soll die Ankündigung sehen?"
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:240
 msgid "Write your announcement:"
 msgstr "Schreiben Sie ihre Ankündigung:"
 
-#: aleksis/core/forms.py:277
+#: aleksis/core/forms.py:279
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr "Sie dürfen keine Ankündigungen erstellen, die nur für die Vergangenheit gültig sind."
 
-#: aleksis/core/forms.py:281
+#: aleksis/core/forms.py:283
 msgid "The from date and time must be earlier then the until date and time."
 msgstr "Das Startdatum und die Startzeit müssen vor dem Enddatum und der Endzeit sein."
 
-#: aleksis/core/forms.py:290
+#: aleksis/core/forms.py:292
 msgid "You need at least one recipient."
 msgstr "Sie benötigen mindestens einen Empfänger."
 
-#: aleksis/core/forms.py:411
+#: aleksis/core/forms.py:401
+msgid "Invitation code"
+msgstr "Einladungscode"
+
+#: aleksis/core/forms.py:402
+msgid "Please enter your invitation code."
+msgstr "Bitte geben Sie Ihren Einladungscode ein."
+
+#: aleksis/core/forms.py:434
 msgid "Who should get the permission?"
 msgstr "Wer soll die Berechtigung erhalten?"
 
-#: aleksis/core/forms.py:412
+#: aleksis/core/forms.py:435
 msgid "On what?"
 msgstr "Auf was?"
 
-#: aleksis/core/forms.py:438
+#: aleksis/core/forms.py:461
 msgid "Select objects which the permission should be granted for:"
-msgstr ""
-"Wählen Sie die Objekte aus, für welche die Berechtigung vergeben werden soll:"
+msgstr "Wählen Sie die Objekte aus, für welche die Berechtigung vergeben werden soll:"
 
-#: aleksis/core/forms.py:441
+#: aleksis/core/forms.py:464
 msgid "Grant the permission for all objects"
 msgstr "Vergebe die Berechtigung für alle Objekte"
 
-#: aleksis/core/forms.py:449
+#: aleksis/core/forms.py:472
 msgid "You must select at least one group or person which should get the permission."
-msgstr ""
-"Sie müssen mindestens eine Gruppe oder Person auswählen, welche die "
-"Berechtigung erhalten soll."
+msgstr "Sie müssen mindestens eine Gruppe oder Person auswählen, welche die Berechtigung erhalten soll."
 
-#: aleksis/core/forms.py:454
+#: aleksis/core/forms.py:477
 msgid "You must grant the permission to all objects and/or to some objects."
-msgstr ""
-"Sie müssen die Berechtigung auf alle Objekte und/oder für einige Objekte "
-"vergeben."
+msgstr "Sie müssen die Berechtigung auf alle Objekte und/oder für einige Objekte vergeben."
 
-#: aleksis/core/forms.py:518
+#: aleksis/core/forms.py:564
+msgid "Adress data"
+msgstr "Adressdaten"
+
+#: aleksis/core/forms.py:576
 msgid "Account data"
 msgstr "Kontodaten"
 
-#: aleksis/core/forms.py:524
-msgid "Consents"
-msgstr "Zustimmungen"
-
-#: aleksis/core/forms.py:531
+#: aleksis/core/forms.py:583
 msgid "Password"
 msgstr "Passwort"
 
-#: aleksis/core/forms.py:537
+#: aleksis/core/forms.py:586
 msgid "Password (again)"
 msgstr "Passwort wiederholen"
 
-#: aleksis/core/forms.py:550
-#, python-brace-format
-msgid "I have read the <a href='{privacy_policy}'>Privacy policy</a> and agree with them."
-msgstr "Ich habe die <a href='{privacy_policy}'>Datenschutzerklärung</a> gelesen und stimme ihr zu."
-
-#: aleksis/core/forms.py:575
-msgid "You must type the same password each time."
-msgstr "Sie müssen zweimal das gleiche Passwort eingeben."
-
-#: aleksis/core/forms.py:720
+#: aleksis/core/forms.py:752
 msgid "No valid selection."
 msgstr "Keine gültige Auswahl."
 
@@ -247,24 +241,20 @@ msgstr "Keine gültige Auswahl."
 msgid "There are unresolved data problems."
 msgstr "Es gibt ungelöste Datenprobleme."
 
-#: aleksis/core/health_checks.py:38
-msgid "The backup folder doesn't exist."
-msgstr "Der Backup-Ordner existiert nicht."
-
-#: aleksis/core/health_checks.py:47
+#: aleksis/core/health_checks.py:44
 #, python-brace-format
 msgid "Last backup {time_gone_since_backup}!"
 msgstr "Letztes Backup: {time_gone_since_backup}!"
 
-#: aleksis/core/health_checks.py:49
+#: aleksis/core/health_checks.py:46
 msgid "No backup found!"
 msgstr "Kein Backup gefunden!"
 
-#: aleksis/core/health_checks.py:76
+#: aleksis/core/health_checks.py:73
 msgid "No backup result found!"
 msgstr "Kein Backupergebnis gefunden!"
 
-#: aleksis/core/health_checks.py:78
+#: aleksis/core/health_checks.py:75
 #, python-brace-format
 msgid "{task.status} - {task.result}"
 msgstr "{task.status} - {task.result}"
@@ -280,34 +270,38 @@ msgstr "Anmelden"
 msgid "Sign up"
 msgstr "Registrieren"
 
-#: aleksis/core/menus.py:24
+#: aleksis/core/menus.py:24 aleksis/core/templates/invitations/enter.html:7
+msgid "Accept invitation"
+msgstr "Einladung akzeptieren"
+
+#: aleksis/core/menus.py:33
 msgid "Dashboard"
 msgstr "Dashboard"
 
-#: aleksis/core/menus.py:32 aleksis/core/models.py:625
-#: aleksis/core/preferences.py:27
+#: aleksis/core/menus.py:41 aleksis/core/models.py:627
+#: aleksis/core/preferences.py:28
 #: aleksis/core/templates/core/notifications.html:4
 #: aleksis/core/templates/core/notifications.html:5
 msgid "Notifications"
 msgstr "Benachrichtigungen"
 
-#: aleksis/core/menus.py:44
+#: aleksis/core/menus.py:53
 msgid "Account"
 msgstr "Konto"
 
-#: aleksis/core/menus.py:51
+#: aleksis/core/menus.py:60
 msgid "Stop impersonation"
 msgstr "Verkleidung beenden"
 
-#: aleksis/core/menus.py:60 aleksis/core/templates/core/base.html:80
+#: aleksis/core/menus.py:69 aleksis/core/templates/core/base.html:80
 msgid "Logout"
 msgstr "Abmelden"
 
-#: aleksis/core/menus.py:66
+#: aleksis/core/menus.py:75
 msgid "2FA"
 msgstr "2FA"
 
-#: aleksis/core/menus.py:74
+#: aleksis/core/menus.py:83
 #: aleksis/core/templates/account/password_change.html:5
 #: aleksis/core/templates/account/password_change.html:6
 #: aleksis/core/templates/account/password_change.html:19
@@ -319,830 +313,850 @@ msgstr "2FA"
 msgid "Change password"
 msgstr "Passwort ändern"
 
-#: aleksis/core/menus.py:86
+#: aleksis/core/menus.py:95
 msgid "Me"
 msgstr "Ich"
 
-#: aleksis/core/menus.py:95
+#: aleksis/core/menus.py:104
 #: aleksis/core/templates/dynamic_preferences/form.html:5
 msgid "Preferences"
 msgstr "Einstellungen"
 
-#: aleksis/core/menus.py:104
+#: aleksis/core/menus.py:113
 msgid "Third-party accounts"
 msgstr "Drittanbieter-Konten"
 
-#: aleksis/core/menus.py:113
+#: aleksis/core/menus.py:122
 #: aleksis/core/templates/oauth2_provider/authorized-tokens.html:5
 #: aleksis/core/templates/oauth2_provider/authorized-tokens.html:6
 msgid "Authorized applications"
 msgstr "Autorisierte Anwendungen"
 
-#: aleksis/core/menus.py:124
+#: aleksis/core/menus.py:133
 msgid "Admin"
 msgstr "Admin"
 
-#: aleksis/core/menus.py:132 aleksis/core/models.py:725
+#: aleksis/core/menus.py:141 aleksis/core/models.py:727
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr "Ankündigungen"
 
-#: aleksis/core/menus.py:143 aleksis/core/models.py:126
+#: aleksis/core/menus.py:152 aleksis/core/models.py:130
 #: aleksis/core/templates/core/school_term/list.html:8
 #: aleksis/core/templates/core/school_term/list.html:9
 msgid "School terms"
 msgstr "Schuljahre"
 
-#: aleksis/core/menus.py:154
+#: aleksis/core/menus.py:163
 #: aleksis/core/templates/core/dashboard_widget/list.html:8
 #: aleksis/core/templates/core/dashboard_widget/list.html:9
 msgid "Dashboard widgets"
 msgstr "Dashboard-Widgets"
 
-#: aleksis/core/menus.py:165
+#: aleksis/core/menus.py:174
 #: aleksis/core/templates/core/management/data_management.html:6
 #: aleksis/core/templates/core/management/data_management.html:7
 msgid "Data management"
 msgstr "Datenverwaltung"
 
-#: aleksis/core/menus.py:176
+#: aleksis/core/menus.py:185
 #: aleksis/core/templates/core/pages/system_status.html:5
 #: aleksis/core/templates/core/pages/system_status.html:7
 msgid "System status"
 msgstr "Systemstatus"
 
-#: aleksis/core/menus.py:187
-msgid "Impersonation"
-msgstr "Verkleidung"
-
-#: aleksis/core/menus.py:198
+#: aleksis/core/menus.py:196
 msgid "Configuration"
 msgstr "Konfiguration"
 
-#: aleksis/core/menus.py:209 aleksis/core/templates/core/data_check/list.html:9
+#: aleksis/core/menus.py:207 aleksis/core/templates/core/data_check/list.html:9
 #: aleksis/core/templates/core/data_check/list.html:10
 msgid "Data checks"
 msgstr "Datenprüfungen"
 
-#: aleksis/core/menus.py:215 aleksis/core/templates/core/perms/list.html:13
+#: aleksis/core/menus.py:213 aleksis/core/templates/core/perms/list.html:13
 #: aleksis/core/templates/core/perms/list.html:14
 msgid "Manage permissions"
 msgstr "Berechtigungen verwalten"
 
-#: aleksis/core/menus.py:226
+#: aleksis/core/menus.py:224
 msgid "Backend Admin"
 msgstr "Backend-Administration"
 
-#: aleksis/core/menus.py:234
+#: aleksis/core/menus.py:232
 #: aleksis/core/templates/oauth2_provider/application/list.html:5
 #: aleksis/core/templates/oauth2_provider/application/list.html:6
 msgid "OAuth2 Applications"
 msgstr "OAuth2-Anwendungen"
 
-#: aleksis/core/menus.py:247
+#: aleksis/core/menus.py:245
 msgid "People"
 msgstr "Leute"
 
-#: aleksis/core/menus.py:278 aleksis/core/models.py:979
+#: aleksis/core/menus.py:276 aleksis/core/models.py:981
 #: aleksis/core/templates/core/group_type/list.html:8
 #: aleksis/core/templates/core/group_type/list.html:9
 msgid "Group types"
 msgstr "Gruppentypen"
 
-#: aleksis/core/menus.py:289
+#: aleksis/core/menus.py:287
 msgid "Groups and child groups"
 msgstr "Gruppen und Kindgruppen"
 
-#: aleksis/core/menus.py:300 aleksis/core/models.py:460
+#: aleksis/core/menus.py:298 aleksis/core/models.py:462
 #: aleksis/core/templates/core/additional_field/list.html:8
 #: aleksis/core/templates/core/additional_field/list.html:9
 msgid "Additional fields"
 msgstr "Zusätzliche Felder"
 
-#: aleksis/core/menus.py:315
+#: aleksis/core/menus.py:309
+msgid "Invite person"
+msgstr "Person einladen"
+
+#: aleksis/core/menus.py:322
 #: aleksis/core/templates/core/group/child_groups.html:7
 #: aleksis/core/templates/core/group/child_groups.html:9
 msgid "Assign child groups to groups"
 msgstr "Kindgruppen zu Gruppen zuordnen"
 
-#: aleksis/core/mixins.py:508
+#: aleksis/core/mixins.py:511
 msgid "Linked school term"
 msgstr "Zugeordnetes Schuljahr"
 
-#: aleksis/core/models.py:66
+#: aleksis/core/models.py:70
 msgid "Boolean (Yes/No)"
 msgstr "Boolean (Ja/Nein)"
 
-#: aleksis/core/models.py:67
+#: aleksis/core/models.py:71
 msgid "Text (one line)"
 msgstr "Text (eine Zeile)"
 
-#: aleksis/core/models.py:69
+#: aleksis/core/models.py:73
 msgid "Date and time"
 msgstr "Datum und Uhrzeit"
 
-#: aleksis/core/models.py:70
+#: aleksis/core/models.py:74
 msgid "Decimal number"
 msgstr "Dezimalzahl"
 
-#: aleksis/core/models.py:71 aleksis/core/models.py:194
+#: aleksis/core/models.py:75 aleksis/core/models.py:198
 msgid "E-mail address"
 msgstr "E-Mail-Adresse"
 
-#: aleksis/core/models.py:72
+#: aleksis/core/models.py:76
 msgid "Integer"
 msgstr "Ganze Zahl"
 
-#: aleksis/core/models.py:73
+#: aleksis/core/models.py:77
 msgid "IP address"
 msgstr "IP-Adresse"
 
-#: aleksis/core/models.py:74
+#: aleksis/core/models.py:78
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr "Boolean oder leer (Ja/Nein/weder)"
 
-#: aleksis/core/models.py:75
+#: aleksis/core/models.py:79
 msgid "Text (multi-line)"
 msgstr "Text (mehrzeilig)"
 
-#: aleksis/core/models.py:77
+#: aleksis/core/models.py:81
 msgid "URL / Link"
 msgstr "URL / Link"
 
-#: aleksis/core/models.py:89 aleksis/core/models.py:948
+#: aleksis/core/models.py:93 aleksis/core/models.py:950
 msgid "Name"
 msgstr "Name"
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:95
 msgid "Start date"
 msgstr "Startdatum"
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:96
 msgid "End date"
 msgstr "Enddatum"
 
-#: aleksis/core/models.py:111
+#: aleksis/core/models.py:115
 msgid "The start date must be earlier than the end date."
 msgstr "Das Startdatum muss vor dem Enddatum liegen."
 
-#: aleksis/core/models.py:118
+#: aleksis/core/models.py:122
 msgid "There is already a school term for this time or a part of this time."
 msgstr "Es gibt bereits ein Schuljahr für diesen Zeitraum oder einen Teilzeitraum."
 
-#: aleksis/core/models.py:147 aleksis/core/models.py:897
+#: aleksis/core/models.py:151 aleksis/core/models.py:899
 msgid "Person"
 msgstr "Person"
 
-#: aleksis/core/models.py:150
+#: aleksis/core/models.py:154
 msgid "Can view address"
 msgstr "Kann Adresse sehen"
 
-#: aleksis/core/models.py:151
+#: aleksis/core/models.py:155
 msgid "Can view contact details"
 msgstr "Kann Kontaktdetails sehen"
 
-#: aleksis/core/models.py:152
+#: aleksis/core/models.py:156
 msgid "Can view photo"
 msgstr "Kann Foto sehen"
 
-#: aleksis/core/models.py:153
+#: aleksis/core/models.py:157
 msgid "Can view persons groups"
 msgstr "Kann Gruppen einer Person sehen"
 
-#: aleksis/core/models.py:154
+#: aleksis/core/models.py:158
 msgid "Can view personal details"
 msgstr "Kann persönliche Daten sehen"
 
-#: aleksis/core/models.py:164
+#: aleksis/core/models.py:168
 msgid "female"
 msgstr "weiblich"
 
-#: aleksis/core/models.py:164
+#: aleksis/core/models.py:168
 msgid "male"
 msgstr "männlich"
 
-#: aleksis/core/models.py:172
+#: aleksis/core/models.py:176 aleksis/core/models.py:1144
 msgid "Linked user"
 msgstr "Verknüpfter Benutzer"
 
-#: aleksis/core/models.py:174
+#: aleksis/core/models.py:178
 msgid "Is person active?"
 msgstr "Ist die Person aktiv?"
 
-#: aleksis/core/models.py:176
+#: aleksis/core/models.py:180
 msgid "First name"
 msgstr "Vorname"
 
-#: aleksis/core/models.py:177
+#: aleksis/core/models.py:181
 msgid "Last name"
 msgstr "Nachname"
 
-#: aleksis/core/models.py:179
+#: aleksis/core/models.py:183
 msgid "Additional name(s)"
 msgstr "Zusätzliche Namen"
 
-#: aleksis/core/models.py:183 aleksis/core/models.py:429
+#: aleksis/core/models.py:187 aleksis/core/models.py:431
 msgid "Short name"
 msgstr "Kurzname"
 
-#: aleksis/core/models.py:186
+#: aleksis/core/models.py:190
 msgid "Street"
 msgstr "Straße"
 
-#: aleksis/core/models.py:187
+#: aleksis/core/models.py:191
 msgid "Street number"
 msgstr "Hausnummer"
 
-#: aleksis/core/models.py:188
+#: aleksis/core/models.py:192
 msgid "Postal code"
 msgstr "Postleitzahl"
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:193
 msgid "Place"
 msgstr "Ort"
 
-#: aleksis/core/models.py:191
+#: aleksis/core/models.py:195
 msgid "Home phone"
 msgstr "Festnetz"
 
-#: aleksis/core/models.py:192
+#: aleksis/core/models.py:196
 msgid "Mobile phone"
 msgstr "Handy"
 
-#: aleksis/core/models.py:196
+#: aleksis/core/models.py:200
 msgid "Date of birth"
 msgstr "Geburtsdatum"
 
-#: aleksis/core/models.py:198
+#: aleksis/core/models.py:201
 msgid "Place of birth"
 msgstr "Geburtsort"
 
-#: aleksis/core/models.py:200
+#: aleksis/core/models.py:202
 msgid "Sex"
 msgstr "Geschlecht"
 
-#: aleksis/core/models.py:202
+#: aleksis/core/models.py:204
 msgid "Photo"
 msgstr "Foto"
 
-#: aleksis/core/models.py:206 aleksis/core/templates/core/person/full.html:138
+#: aleksis/core/models.py:208 aleksis/core/templates/core/person/full.html:145
 msgid "Guardians / Parents"
 msgstr "Erziehungsberechtigte / Eltern"
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:215
 msgid "Primary group"
 msgstr "Primärgruppe"
 
-#: aleksis/core/models.py:216 aleksis/core/models.py:583
-#: aleksis/core/models.py:607 aleksis/core/models.py:692
-#: aleksis/core/models.py:972 aleksis/core/templates/core/person/full.html:121
+#: aleksis/core/models.py:218 aleksis/core/models.py:585
+#: aleksis/core/models.py:609 aleksis/core/models.py:694
+#: aleksis/core/models.py:974 aleksis/core/templates/core/person/full.html:128
 msgid "Description"
 msgstr "Beschreibung"
 
-#: aleksis/core/models.py:384
+#: aleksis/core/models.py:386
 msgid "Title of field"
 msgstr "Feldtitel"
 
-#: aleksis/core/models.py:386
+#: aleksis/core/models.py:388
 msgid "Type of field"
 msgstr "Feldtyp"
 
-#: aleksis/core/models.py:393
+#: aleksis/core/models.py:395
 msgid "Addtitional field for groups"
 msgstr "Zusätzliche Felder für Gruppen"
 
-#: aleksis/core/models.py:394
+#: aleksis/core/models.py:396
 msgid "Addtitional fields for groups"
 msgstr "Zusätzliche Felder für Gruppen"
 
-#: aleksis/core/models.py:414
+#: aleksis/core/models.py:416
 msgid "Can assign child groups to groups"
 msgstr "Kann Kindgruppen zu Gruppen zuordnen"
 
-#: aleksis/core/models.py:415
+#: aleksis/core/models.py:417
 msgid "Can view statistics about group."
 msgstr "Kann Statistiken über Gruppen sehen."
 
-#: aleksis/core/models.py:427
+#: aleksis/core/models.py:429
 msgid "Long name"
 msgstr "Langname"
 
-#: aleksis/core/models.py:437 aleksis/core/templates/core/group/full.html:85
+#: aleksis/core/models.py:439 aleksis/core/templates/core/group/full.html:85
 msgid "Members"
 msgstr "Mitglieder"
 
-#: aleksis/core/models.py:440 aleksis/core/templates/core/group/full.html:82
+#: aleksis/core/models.py:442 aleksis/core/templates/core/group/full.html:82
 msgid "Owners"
 msgstr "Leiter/-innen"
 
-#: aleksis/core/models.py:447 aleksis/core/templates/core/group/full.html:55
+#: aleksis/core/models.py:449 aleksis/core/templates/core/group/full.html:55
 msgid "Parent groups"
 msgstr "Ãœbergeordnete Gruppen"
 
-#: aleksis/core/models.py:455
+#: aleksis/core/models.py:457
 msgid "Type of group"
 msgstr "Gruppentyp"
 
-#: aleksis/core/models.py:582 aleksis/core/models.py:606
-#: aleksis/core/models.py:691
+#: aleksis/core/models.py:584 aleksis/core/models.py:608
+#: aleksis/core/models.py:693
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr "Titel"
 
-#: aleksis/core/models.py:585
+#: aleksis/core/models.py:587
 msgid "Application"
 msgstr "Anwendung"
 
-#: aleksis/core/models.py:591
+#: aleksis/core/models.py:593
 msgid "Activity"
 msgstr "Aktivität"
 
-#: aleksis/core/models.py:592
+#: aleksis/core/models.py:594
 msgid "Activities"
 msgstr "Aktivitäten"
 
-#: aleksis/core/models.py:598
+#: aleksis/core/models.py:600
 msgid "Sender"
 msgstr "Absender"
 
-#: aleksis/core/models.py:603
+#: aleksis/core/models.py:605
 msgid "Recipient"
 msgstr "Empfänger"
 
-#: aleksis/core/models.py:608 aleksis/core/models.py:949
+#: aleksis/core/models.py:610 aleksis/core/models.py:951
 msgid "Link"
 msgstr "Link"
 
-#: aleksis/core/models.py:610
+#: aleksis/core/models.py:612
 msgid "Read"
 msgstr "Gelesen"
 
-#: aleksis/core/models.py:611
+#: aleksis/core/models.py:613
 msgid "Sent"
 msgstr "Versandt"
 
-#: aleksis/core/models.py:624
+#: aleksis/core/models.py:626
 msgid "Notification"
 msgstr "Benachrichtigung"
 
-#: aleksis/core/models.py:693
+#: aleksis/core/models.py:695
 msgid "Link to detailed view"
 msgstr "Link zur detaillierten Ansicht"
 
-#: aleksis/core/models.py:696
+#: aleksis/core/models.py:698
 msgid "Date and time from when to show"
 msgstr "Datum und Uhrzeit des Anzeigestarts"
 
-#: aleksis/core/models.py:699
+#: aleksis/core/models.py:701
 msgid "Date and time until when to show"
 msgstr "Anzeigezeitraum"
 
-#: aleksis/core/models.py:724
+#: aleksis/core/models.py:726
 msgid "Announcement"
 msgstr "Ankündigung"
 
-#: aleksis/core/models.py:762
+#: aleksis/core/models.py:764
 msgid "Announcement recipient"
 msgstr "Empfänger der Ankündigung"
 
-#: aleksis/core/models.py:763
+#: aleksis/core/models.py:765
 msgid "Announcement recipients"
 msgstr "Empfänger der Ankündigung"
 
-#: aleksis/core/models.py:818
+#: aleksis/core/models.py:820
 msgid "Widget Title"
 msgstr "Widget-Titel"
 
-#: aleksis/core/models.py:819
+#: aleksis/core/models.py:821
 msgid "Activate Widget"
 msgstr "Widget aktivieren"
 
-#: aleksis/core/models.py:820
+#: aleksis/core/models.py:822
 msgid "Widget is broken"
 msgstr "Widget ist kaputt"
 
-#: aleksis/core/models.py:823
+#: aleksis/core/models.py:825
 msgid "Size on mobile devices"
 msgstr "Größe auf Mobilgeräten"
 
-#: aleksis/core/models.py:824
+#: aleksis/core/models.py:826
 msgid "<= 600 px, 12 columns"
 msgstr "<= 600 px, 12 Spalten"
 
-#: aleksis/core/models.py:829
+#: aleksis/core/models.py:831
 msgid "Size on tablet devices"
 msgstr "Größe auf Tablets"
 
-#: aleksis/core/models.py:830
+#: aleksis/core/models.py:832
 msgid "> 600 px, 12 columns"
 msgstr "> 600px, 12 Spalten"
 
-#: aleksis/core/models.py:835
+#: aleksis/core/models.py:837
 msgid "Size on desktop devices"
 msgstr "Größe auf Desktopgeräten"
 
-#: aleksis/core/models.py:836
+#: aleksis/core/models.py:838
 msgid "> 992 px, 12 columns"
 msgstr "> 992 px, 12 Spalten"
 
-#: aleksis/core/models.py:841
+#: aleksis/core/models.py:843
 msgid "Size on large desktop devices"
 msgstr "Größe auf großen Desktopgeräten"
 
-#: aleksis/core/models.py:842
+#: aleksis/core/models.py:844
 msgid "> 1200 px>, 12 columns"
 msgstr "> 1200 px, 12 Spalten"
 
-#: aleksis/core/models.py:873
+#: aleksis/core/models.py:875
 msgid "Can edit default dashboard"
 msgstr "Kann Standarddashboard bearbeiten"
 
-#: aleksis/core/models.py:874
+#: aleksis/core/models.py:876
 msgid "Dashboard Widget"
 msgstr "Dashboard-Widget"
 
-#: aleksis/core/models.py:875
+#: aleksis/core/models.py:877
 msgid "Dashboard Widgets"
 msgstr "Dashboard-Widgets"
 
-#: aleksis/core/models.py:881
+#: aleksis/core/models.py:883
 msgid "URL"
 msgstr "URL"
 
-#: aleksis/core/models.py:882
+#: aleksis/core/models.py:884
 msgid "Icon URL"
 msgstr "Symbol-URL"
 
-#: aleksis/core/models.py:888
+#: aleksis/core/models.py:890
 msgid "External link widget"
 msgstr "Externer-Link-Widget"
 
-#: aleksis/core/models.py:889
+#: aleksis/core/models.py:891
 msgid "External link widgets"
 msgstr "Externer-Link-Widgets"
 
-#: aleksis/core/models.py:894
+#: aleksis/core/models.py:896
 msgid "Dashboard widget"
 msgstr "Dashboard-Widget"
 
-#: aleksis/core/models.py:899
+#: aleksis/core/models.py:901
 msgid "Order"
 msgstr "Reihenfolge"
 
-#: aleksis/core/models.py:900
+#: aleksis/core/models.py:902
 msgid "Part of the default dashboard"
 msgstr "Teil des Standarddashboards"
 
-#: aleksis/core/models.py:915
+#: aleksis/core/models.py:917
 msgid "Dashboard widget order"
 msgstr "Reihenfolge der Dashboard-Widgets"
 
-#: aleksis/core/models.py:916
+#: aleksis/core/models.py:918
 msgid "Dashboard widget orders"
 msgstr "Reihenfolgen der Dashboard-Widgets"
 
-#: aleksis/core/models.py:922
+#: aleksis/core/models.py:924
 msgid "Menu ID"
 msgstr "Menü-ID"
 
-#: aleksis/core/models.py:935
+#: aleksis/core/models.py:937
 msgid "Custom menu"
 msgstr "Benutzerdefiniertes Menü"
 
-#: aleksis/core/models.py:936
+#: aleksis/core/models.py:938
 msgid "Custom menus"
 msgstr "Benutzerdefinierte Menüs"
 
-#: aleksis/core/models.py:946
+#: aleksis/core/models.py:948
 msgid "Menu"
 msgstr "Menü"
 
-#: aleksis/core/models.py:950
+#: aleksis/core/models.py:952
 msgid "Icon"
 msgstr "Symbol"
 
-#: aleksis/core/models.py:956
+#: aleksis/core/models.py:958
 msgid "Custom menu item"
 msgstr "Benutzerdefiniertes Menüelement"
 
-#: aleksis/core/models.py:957
+#: aleksis/core/models.py:959
 msgid "Custom menu items"
 msgstr "Benutzerdefinierte Menüelemente"
 
-#: aleksis/core/models.py:971
+#: aleksis/core/models.py:973
 msgid "Title of type"
 msgstr "Titel des Typs"
 
-#: aleksis/core/models.py:978 aleksis/core/templates/core/group/full.html:47
+#: aleksis/core/models.py:980 aleksis/core/templates/core/group/full.html:47
 msgid "Group type"
 msgstr "Gruppentyp"
 
-#: aleksis/core/models.py:992
+#: aleksis/core/models.py:994
 msgid "Can view system status"
 msgstr "Kann Systemstatus sehen"
 
-#: aleksis/core/models.py:993
+#: aleksis/core/models.py:995
 msgid "Can manage data"
 msgstr "Kann Daten verwalten"
 
-#: aleksis/core/models.py:994
+#: aleksis/core/models.py:996
 msgid "Can impersonate"
 msgstr "Kann sich verkleiden"
 
-#: aleksis/core/models.py:995
+#: aleksis/core/models.py:997
 msgid "Can use search"
 msgstr "Kann Suche benutzen"
 
-#: aleksis/core/models.py:996
+#: aleksis/core/models.py:998
 msgid "Can change site preferences"
 msgstr "Kann Konfiguration ändern"
 
-#: aleksis/core/models.py:997
+#: aleksis/core/models.py:999
 msgid "Can change person preferences"
 msgstr "Kann Einstellungen einer Person verändern"
 
-#: aleksis/core/models.py:998
+#: aleksis/core/models.py:1000
 msgid "Can change group preferences"
 msgstr "Kann Einstellungen einer Gruppe verändern"
 
-#: aleksis/core/models.py:999
+#: aleksis/core/models.py:1001
 msgid "Can test PDF generation"
 msgstr "Kann die PDF-Generierung testen"
 
-#: aleksis/core/models.py:1035
+#: aleksis/core/models.py:1037
 msgid "Related data check task"
 msgstr "Zugehörige Datenprüfungsaufgabe"
 
-#: aleksis/core/models.py:1043
+#: aleksis/core/models.py:1045
 msgid "Issue solved"
 msgstr "Problem gelöst"
 
-#: aleksis/core/models.py:1044
+#: aleksis/core/models.py:1046
 msgid "Notification sent"
 msgstr "Benachrichtigung gesendet"
 
-#: aleksis/core/models.py:1057
+#: aleksis/core/models.py:1059
 msgid "Data check result"
 msgstr "Datenprüfungsergebnis"
 
-#: aleksis/core/models.py:1058
+#: aleksis/core/models.py:1060
 msgid "Data check results"
 msgstr "Datenprüfungsergebnisse"
 
-#: aleksis/core/models.py:1060
+#: aleksis/core/models.py:1062
 msgid "Can run data checks"
 msgstr "Kann Datenprüfungen ausführen"
 
-#: aleksis/core/models.py:1061
+#: aleksis/core/models.py:1063
 msgid "Can solve data check problems"
 msgstr "Kann Datenprüfungsprobleme lösen"
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1070
+msgid "E-Mail address"
+msgstr "E-Mail-Adresse"
+
+#: aleksis/core/models.py:1094
 msgid "Owner"
 msgstr "Leiter"
 
-#: aleksis/core/models.py:1080
+#: aleksis/core/models.py:1098
 msgid "File expires at"
 msgstr "Datei abgelaufen am"
 
-#: aleksis/core/models.py:1082
+#: aleksis/core/models.py:1100
 msgid "Generated HTML file"
 msgstr "Generierte HTML-Datei"
 
-#: aleksis/core/models.py:1084
+#: aleksis/core/models.py:1102
 msgid "Generated PDF file"
 msgstr "Generierte PDF-Datei"
 
-#: aleksis/core/models.py:1091
+#: aleksis/core/models.py:1109
 msgid "PDF file"
 msgstr "PDF-Datei"
 
-#: aleksis/core/models.py:1092
+#: aleksis/core/models.py:1110
 msgid "PDF files"
 msgstr "PDF-Dateien"
 
-#: aleksis/core/models.py:1097
+#: aleksis/core/models.py:1115
 msgid "Task result"
 msgstr "Task-Ergebnis"
 
-#: aleksis/core/models.py:1100
+#: aleksis/core/models.py:1118
 msgid "Task user"
 msgstr "Task-Benutzer"
 
-#: aleksis/core/models.py:1112
+#: aleksis/core/models.py:1130
 msgid "Task user assignment"
 msgstr "Task-Benutzer-Zuordnung"
 
-#: aleksis/core/models.py:1113
+#: aleksis/core/models.py:1131
 msgid "Task user assignments"
 msgstr "Task-Benutzer-Zuordnungen"
 
-#: aleksis/core/models.py:1127
+#: aleksis/core/models.py:1147
+msgid "Additional attributes"
+msgstr "Zusätzliche Attribute"
+
+#: aleksis/core/models.py:1185
 msgid "Allowed scopes that clients can request"
 msgstr "Erlaubte Scopes, die ein Client anfordern kann"
 
-#: aleksis/core/preferences.py:23
+#: aleksis/core/preferences.py:24
 msgid "General"
 msgstr "Allgemein"
 
-#: aleksis/core/preferences.py:24
+#: aleksis/core/preferences.py:25
 msgid "School"
 msgstr "Schule"
 
-#: aleksis/core/preferences.py:25
+#: aleksis/core/preferences.py:26
 msgid "Theme"
 msgstr "Theme"
 
-#: aleksis/core/preferences.py:26
+#: aleksis/core/preferences.py:27
 msgid "Mail"
 msgstr "E-Mail"
 
-#: aleksis/core/preferences.py:28
+#: aleksis/core/preferences.py:29
 msgid "Footer"
 msgstr "Fußbereich"
 
-#: aleksis/core/preferences.py:29
+#: aleksis/core/preferences.py:30
 msgid "Accounts"
 msgstr "Konten"
 
-#: aleksis/core/preferences.py:30
+#: aleksis/core/preferences.py:31
 msgid "Authentication"
 msgstr "Authentifizierung"
 
-#: aleksis/core/preferences.py:31
+#: aleksis/core/preferences.py:32
 msgid "Internationalisation"
 msgstr "Internationalisierung"
 
-#: aleksis/core/preferences.py:42
+#: aleksis/core/preferences.py:43
 msgid "Site title"
 msgstr "Seitentitel"
 
-#: aleksis/core/preferences.py:53
+#: aleksis/core/preferences.py:54
 msgid "Site description"
 msgstr "Seitenbeschreibung"
 
-#: aleksis/core/preferences.py:64
+#: aleksis/core/preferences.py:65
 msgid "Primary colour"
 msgstr "Primärfarbe"
 
-#: aleksis/core/preferences.py:76
+#: aleksis/core/preferences.py:77
 msgid "Secondary colour"
 msgstr "Akzentfarbe"
 
-#: aleksis/core/preferences.py:87
+#: aleksis/core/preferences.py:88
 msgid "Logo"
 msgstr "Logo"
 
-#: aleksis/core/preferences.py:97
+#: aleksis/core/preferences.py:98
 msgid "Favicon"
 msgstr "Favicon"
 
-#: aleksis/core/preferences.py:107
+#: aleksis/core/preferences.py:108
 msgid "PWA-Icon"
 msgstr "PWA-Icon"
 
-#: aleksis/core/preferences.py:118
+#: aleksis/core/preferences.py:119
 msgid "Mail out name"
 msgstr "Ausgangsmailname"
 
-#: aleksis/core/preferences.py:129
+#: aleksis/core/preferences.py:130
 msgid "Mail out address"
 msgstr "E-Mail-Ausgangsadresse"
 
-#: aleksis/core/preferences.py:141
+#: aleksis/core/preferences.py:142
 msgid "Link to privacy policy"
 msgstr "Link zur Datenschutzerklärung"
 
-#: aleksis/core/preferences.py:153
+#: aleksis/core/preferences.py:154
 msgid "Link to imprint"
 msgstr "Link zum Impressum"
 
-#: aleksis/core/preferences.py:165
+#: aleksis/core/preferences.py:166
 msgid "Name format for addressing"
 msgstr "Namensformat für Anreden"
 
-#: aleksis/core/preferences.py:181
+#: aleksis/core/preferences.py:182
 msgid "Channels to use for notifications"
 msgstr "Aktivierte Benachrichtungskanäle"
 
-#: aleksis/core/preferences.py:193
+#: aleksis/core/preferences.py:194
 msgid "Regular expression to match primary group, e.g. '^Class .*'"
 msgstr "Regulärer Ausdruck um Primärgruppen zu finden, z. B.  '^Class .*'"
 
-#: aleksis/core/preferences.py:204
+#: aleksis/core/preferences.py:205
 msgid "Field on person to match primary group against"
 msgstr "Feld um Primärgruppen zu finden"
 
-#: aleksis/core/preferences.py:216
+#: aleksis/core/preferences.py:217
 msgid "Automatically create new persons for new users"
 msgstr "Erstelle automatisch neue Personen für neue Benutzer"
 
-#: aleksis/core/preferences.py:225
+#: aleksis/core/preferences.py:226
 msgid "Automatically link existing persons to new users by their e-mail address"
 msgstr "Verknüpfe existierende Personen automatisch mit neuen Personen anhand ihrer E-Mail-Adresse"
 
-#: aleksis/core/preferences.py:236
+#: aleksis/core/preferences.py:237
 msgid "Display name of the school"
 msgstr "Sichtbarer Name der Schule"
 
-#: aleksis/core/preferences.py:247
+#: aleksis/core/preferences.py:248
 msgid "Official name of the school, e.g. as given by supervisory authority"
 msgstr "Offizieller Name der Schule, wie er z.B. von der Behörde vorgegeben ist"
 
-#: aleksis/core/preferences.py:255
+#: aleksis/core/preferences.py:256
 msgid "Allow users to change their passwords"
 msgstr "Erlaube Benutzern, ihr Passwort zu ändern"
 
-#: aleksis/core/preferences.py:263
+#: aleksis/core/preferences.py:264
 msgid "Enable signup"
 msgstr "Registrierung aktivieren"
 
-#: aleksis/core/preferences.py:274
+#: aleksis/core/preferences.py:272
+msgid "Enable invitations"
+msgstr "Einladungen aktivieren"
+
+#: aleksis/core/preferences.py:280
+msgid "Length of invite code. (Default 3: abcde-acbde-abcde)"
+msgstr "Länge des Einladungscodes. (Standard: 3: abcde-acbde-abcde)"
+
+#: aleksis/core/preferences.py:288
+msgid "Size of packets. (Default 5: abcde)"
+msgstr "Größe der Pakete. (Standard 5: abcde)"
+
+#: aleksis/core/preferences.py:298
 msgid "Allowed Grant Flows for OAuth applications"
 msgstr "Erlaubte Grant Flows für OAuth-Anwendungen"
 
-#: aleksis/core/preferences.py:287
+#: aleksis/core/preferences.py:311
 msgid "Available languages"
 msgstr "Verfügbare Sprachen"
 
-#: aleksis/core/preferences.py:299
+#: aleksis/core/preferences.py:323
 msgid "Send emails if data checks detect problems"
 msgstr "E-Mails versenden, wenn Datenprüfungen Probleme finden"
 
-#: aleksis/core/preferences.py:310
+#: aleksis/core/preferences.py:334
 msgid "Email recipients for data checks problem emails"
 msgstr "E-Mailempfänger für Datenprüfungsproblem-E-Mails"
 
-#: aleksis/core/preferences.py:321
+#: aleksis/core/preferences.py:345
 msgid "Email recipient groups for data checks problem emails"
 msgstr "E-Mail-Empfängergruppen für Datenprüfungsproblem-E-Mails"
 
-#: aleksis/core/preferences.py:330
+#: aleksis/core/preferences.py:354
 msgid "Show dashboard to users without login"
 msgstr "Zeige Dashboard für Benutzer ohne Login"
 
-#: aleksis/core/preferences.py:339
+#: aleksis/core/preferences.py:363
 msgid "Allow users to edit their dashboard"
 msgstr "Erlaube Benutzern, ihr Dashboard zu bearbeiten"
 
-#: aleksis/core/preferences.py:350
+#: aleksis/core/preferences.py:374
 msgid "Fields on person model which are editable by themselves."
 msgstr "Felder des Personen-Models welche von ihnen selbst editierbar sind."
 
-#: aleksis/core/preferences.py:364
+#: aleksis/core/preferences.py:388
 msgid "Editable fields on person model which should trigger a notification on change"
 msgstr "Editierbare Felder des Personen-Models welche eine Benachrichtigung für Änderungen auslösen soll"
 
-#: aleksis/core/preferences.py:377
+#: aleksis/core/preferences.py:401
 msgid "Contact for notification if a person changes their data"
 msgstr "Kontakt für Benachrichtigung, wenn eine Person ihre Daten ändert"
 
-#: aleksis/core/preferences.py:387
+#: aleksis/core/preferences.py:411
 msgid "PDF file expiration duration"
 msgstr "PDF-Datei-Ablaufdauer"
 
-#: aleksis/core/preferences.py:388
+#: aleksis/core/preferences.py:412
 msgid "in minutes"
 msgstr "in Minuten"
 
-#: aleksis/core/preferences.py:398
+#: aleksis/core/preferences.py:422
 msgid "Automatically update the dashboard and its widgets"
 msgstr "Automatisch das Dashboard und seine Widgets aktualisieren"
 
-#: aleksis/core/preferences.py:408
+#: aleksis/core/preferences.py:432
 msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr "Automatisch das Dashboard und seine Widgets aktualisieren (auf der ganzen Seite)"
 
-#: aleksis/core/settings.py:474
+#: aleksis/core/settings.py:507
 msgid "English"
 msgstr "Englisch"
 
-#: aleksis/core/settings.py:475
+#: aleksis/core/settings.py:508
 msgid "German"
 msgstr "Deutsch"
 
-#: aleksis/core/tables.py:19
+#: aleksis/core/tables.py:24
 #: aleksis/core/templates/core/announcement/list.html:36
 #: aleksis/core/templates/core/group/full.html:24
-#: aleksis/core/templates/core/person/full.html:23
+#: aleksis/core/templates/core/person/full.html:24
 #: aleksis/core/templates/oauth2_provider/application/detail.html:17
 msgid "Edit"
 msgstr "Bearbeiten"
 
-#: aleksis/core/tables.py:21 aleksis/core/tables.py:89
-#: aleksis/core/tables.py:105
+#: aleksis/core/tables.py:26 aleksis/core/tables.py:94
+#: aleksis/core/tables.py:137
 #: aleksis/core/templates/core/announcement/list.html:22
 msgid "Actions"
 msgstr "Aktionen"
 
-#: aleksis/core/tables.py:56 aleksis/core/tables.py:57
-#: aleksis/core/tables.py:71 aleksis/core/tables.py:87
-#: aleksis/core/tables.py:103
+#: aleksis/core/tables.py:61 aleksis/core/tables.py:62
+#: aleksis/core/tables.py:76 aleksis/core/tables.py:92
+#: aleksis/core/tables.py:135
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/group/full.html:31
 #: aleksis/core/templates/core/pages/delete.html:22
-#: aleksis/core/templates/core/person/full.html:30
+#: aleksis/core/templates/core/person/full.html:31
 #: aleksis/core/templates/oauth2_provider/application/detail.html:21
 msgid "Delete"
 msgstr "Löschen"
@@ -1237,11 +1251,11 @@ msgstr ""
 msgid "Account inactive"
 msgstr "Konto inaktiv"
 
-#: aleksis/core/templates/account/account_inactive.html:13
+#: aleksis/core/templates/account/account_inactive.html:14
 msgid "Account inactive."
 msgstr "Konto inaktiv."
 
-#: aleksis/core/templates/account/account_inactive.html:15
+#: aleksis/core/templates/account/account_inactive.html:17
 msgid ""
 "\n"
 "            This account is currently inactive. If you think this is an\n"
@@ -1417,11 +1431,11 @@ msgstr "Haben Sie bereits ein Konto? Dann <a href=\"%(login_url)s\">melden Sie s
 msgid "Signup closed"
 msgstr "Registrierung geschlossen"
 
-#: aleksis/core/templates/account/signup_closed.html:13
+#: aleksis/core/templates/account/signup_closed.html:14
 msgid "Signup closed."
 msgstr "Registrierung geschlossen."
 
-#: aleksis/core/templates/account/signup_closed.html:15
+#: aleksis/core/templates/account/signup_closed.html:17
 msgid ""
 "\n"
 "            This sign up is currently closed. If you think this is an\n"
@@ -1472,11 +1486,6 @@ msgstr ""
 "Bitte kontaktieren Sie uns, wenn Sie diese nicht binnen weniger Minuten erhalten.\n"
 "          "
 
-#: aleksis/core/templates/account/verification_sent.html:30
-#, python-format
-msgid "<strong>Note:</strong> you can still <a href=\"%(email_url)s\">change your e-mail address</a>"
-msgstr "<strong>Hinweis:</strong> Sie können immer noch <a href=\"%(email_url)s\"> Ihre E-Mail-Adresse ändern</a>"
-
 #: aleksis/core/templates/core/additional_field/edit.html:6
 #: aleksis/core/templates/core/additional_field/edit.html:7
 msgid "Edit additional field"
@@ -1525,11 +1534,11 @@ msgid "Logged in as"
 msgstr "Angemeldet als"
 
 #: aleksis/core/templates/core/base.html:175
-msgid "About AlekSIS — The Free School Information System"
-msgstr "Über AlekSIS — The Free School Information System"
+msgid "About AlekSIS® — The Free School Information System"
+msgstr "Über AlekSIS® — The Free School Information System"
 
 #: aleksis/core/templates/core/base.html:183
-msgid "Impress"
+msgid "Imprint"
 msgstr "Impressum"
 
 #: aleksis/core/templates/core/base.html:191
@@ -1537,8 +1546,8 @@ msgid "Privacy Policy"
 msgstr "Datenschutzerklärung"
 
 #: aleksis/core/templates/core/base_print.html:72
-msgid "Powered by AlekSIS"
-msgstr "Betrieben mit AlekSIS"
+msgid "Powered by AlekSIS®"
+msgstr "Betrieben mit AlekSIS®"
 
 #: aleksis/core/templates/core/dashboard_widget/create.html:8
 #: aleksis/core/templates/core/dashboard_widget/create.html:12
@@ -1764,7 +1773,7 @@ msgstr "Zurück"
 #: aleksis/core/templates/core/group/child_groups.html:134
 #: aleksis/core/templates/two_factor/_wizard_actions.html:26
 msgid "Next"
-msgstr "Weiter"
+msgstr "Nächste"
 
 #: aleksis/core/templates/core/group/child_groups.html:106
 #: aleksis/core/templates/core/group/child_groups.html:141
@@ -1783,7 +1792,7 @@ msgid "Edit group"
 msgstr "Gruppe editieren"
 
 #: aleksis/core/templates/core/group/full.html:38
-#: aleksis/core/templates/core/person/full.html:37
+#: aleksis/core/templates/core/person/full.html:38
 msgid "Change preferences"
 msgstr "Einstellungen ändern"
 
@@ -1876,41 +1885,54 @@ msgid "No notifications available yet."
 msgstr "Aktuell keine Benachrichtigungen verfügbar."
 
 #: aleksis/core/templates/core/pages/about.html:6
+msgid "About AlekSIS®"
+msgstr "Über AlekSIS®"
+
+#: aleksis/core/templates/core/pages/about.html:7
+msgid "AlekSIS® – The Free School Information System"
+msgstr "AlekSIS® – The Free School Information System"
+
 #: aleksis/core/templates/core/pages/about.html:15
 msgid "About AlekSIS"
 msgstr "Ãœber AlekSIS"
 
-#: aleksis/core/templates/core/pages/about.html:7
-msgid "AlekSIS – The Free School Information System"
-msgstr "AlekSIS – The Free School Information System"
-
 #: aleksis/core/templates/core/pages/about.html:17
 msgid ""
 "\n"
-"              This platform is powered by AlekSIS, a web-based school information system (SIS) which can be used\n"
+"              This platform is powered by AlekSIS®, a web-based school information system (SIS) which can be used\n"
 "              to manage and/or publish organisational artifacts of educational institutions. AlekSIS is free software and\n"
 "              can be used by anyone.\n"
 "            "
 msgstr ""
 "\n"
-"              Diese Plattform wird mit AlekSIS, einem webbasierten Schulinformationssystem (SIS), \n"
+"              Diese Plattform wird mit AlekSIS®, einem webbasierten Schulinformationssystem (SIS), \n"
 "welches für die Verwaltung und/oder Veröffentlichung von Bildungseinrichtungen verwendet werden kann.\n"
 "AlekSIS ist freie Software und kann von jedem benutzt werden.\n"
 "            "
 
-#: aleksis/core/templates/core/pages/about.html:25
+#: aleksis/core/templates/core/pages/about.html:24
+msgid ""
+"\n"
+"              AlekSIS® is a registered trademark of the AlekSIS open source project, represented by Teckids e.V.\n"
+"            "
+msgstr ""
+"\n"
+"              AlekSIS® ist eine eingetragene Wortmarke des Open-Source-Projektes AlekSIS, vertreten durch den Teckids e.V.\n"
+"            "
+
+#: aleksis/core/templates/core/pages/about.html:30
 msgid "Website of AlekSIS"
 msgstr "Website von AlekSIS"
 
-#: aleksis/core/templates/core/pages/about.html:26
+#: aleksis/core/templates/core/pages/about.html:31
 msgid "Source code"
 msgstr "Quellcode"
 
-#: aleksis/core/templates/core/pages/about.html:35
+#: aleksis/core/templates/core/pages/about.html:40
 msgid "Licence information"
 msgstr "Lizenzinformationen"
 
-#: aleksis/core/templates/core/pages/about.html:37
+#: aleksis/core/templates/core/pages/about.html:42
 msgid ""
 "\n"
 "              The core and the official apps of AlekSIS are licenced under the EUPL, version 1.2 or later. For licence\n"
@@ -1924,23 +1946,23 @@ msgstr ""
 "sind wie folgt markiert:\n"
 "            "
 
-#: aleksis/core/templates/core/pages/about.html:45
+#: aleksis/core/templates/core/pages/about.html:50
 msgid "Free/Open Source Licence"
 msgstr "Freie/Open Source Lizenz"
 
-#: aleksis/core/templates/core/pages/about.html:46
+#: aleksis/core/templates/core/pages/about.html:51
 msgid "Other Licence"
 msgstr "Andere Lizenz"
 
-#: aleksis/core/templates/core/pages/about.html:50
+#: aleksis/core/templates/core/pages/about.html:55
 msgid "Full licence text"
 msgstr "Kompletter Lizenztext"
 
-#: aleksis/core/templates/core/pages/about.html:51
+#: aleksis/core/templates/core/pages/about.html:56
 msgid "More information about the EUPL"
 msgstr "Weitere Informationen über die EUPL"
 
-#: aleksis/core/templates/core/pages/about.html:90
+#: aleksis/core/templates/core/pages/about.html:95
 #, python-format
 msgid ""
 "\n"
@@ -2220,17 +2242,19 @@ msgstr "Person erstellen"
 msgid "Edit person"
 msgstr "Person editieren"
 
-#: aleksis/core/templates/core/person/full.html:44
-#: aleksis/core/templates/impersonate/list_users.html:7
-#: aleksis/core/templates/impersonate/list_users.html:8
+#: aleksis/core/templates/core/person/full.html:45
 msgid "Impersonate"
 msgstr "Verkleiden"
 
-#: aleksis/core/templates/core/person/full.html:50
+#: aleksis/core/templates/core/person/full.html:51
+msgid "Invite user"
+msgstr "Benutzer einladen"
+
+#: aleksis/core/templates/core/person/full.html:57
 msgid "Contact details"
 msgstr "Kontaktdetails"
 
-#: aleksis/core/templates/core/person/full.html:131
+#: aleksis/core/templates/core/person/full.html:138
 msgid "Children"
 msgstr "Kinder"
 
@@ -2270,6 +2294,53 @@ msgstr "Einstellungen für %(instance)s"
 msgid "Save preferences"
 msgstr "Einstellungen speichern"
 
+#: aleksis/core/templates/invitations/enter.html:21
+msgid "Accept your invitation"
+msgstr "Ihre Einladung akzeptieren"
+
+#: aleksis/core/templates/invitations/enter.html:25
+msgid ""
+"\n"
+"                Please enter your invitation code to register\n"
+"                your new user account:\n"
+"              "
+msgstr ""
+"\n"
+"                Bitte geben Sie Ihren Einladungscode ein, \n"
+"um Ihr neues Benutzerkonto zu registrieren:\n"
+"              "
+
+#: aleksis/core/templates/invitations/enter.html:37
+msgid "Accept invite"
+msgstr "Einladung akzeptieren"
+
+#: aleksis/core/templates/invitations/forms/_invite.html:9
+#: aleksis/core/templates/invitations/forms/_invite.html:10
+#: aleksis/core/templates/invitations/forms/_invite.html:21
+msgid "Invite"
+msgstr "Einladen"
+
+#: aleksis/core/templates/invitations/forms/_invite.html:17
+msgid "Invite by email address"
+msgstr "Mit E-Mail-Adresse einladen"
+
+#: aleksis/core/templates/invitations/forms/_invite.html:26
+msgid "Generate invitation code"
+msgstr "Einladungscode generieren"
+
+#: aleksis/core/templates/invitations/forms/_invite.html:29
+msgid "Generate code"
+msgstr "Code generieren"
+
+#: aleksis/core/templates/invitations/forms/_invite.html:33
+msgid "Invitations"
+msgstr "Einladungen"
+
+#: aleksis/core/templates/invitations/messages/invite_accepted.txt:3
+#, python-format
+msgid "The invitation for %(email)s has been accepted."
+msgstr "Die Einladung für %(email)s wurde akzeptiert."
+
 #: aleksis/core/templates/oauth2_provider/application/create.html:5
 #: aleksis/core/templates/oauth2_provider/application/create.html:6
 msgid "Register OAuth2 Application"
@@ -3075,151 +3146,187 @@ msgstr "E-Mail"
 msgid "SMS"
 msgstr "SMS"
 
-#: aleksis/core/util/pdf.py:105
+#: aleksis/core/util/pdf.py:113
 msgid "Progress: Generate PDF file"
 msgstr "Fortschritt: PDF-Datei generieren"
 
-#: aleksis/core/util/pdf.py:106
+#: aleksis/core/util/pdf.py:114
 msgid "Generating PDF file …"
 msgstr "PDF-Datei wird generiert …"
 
-#: aleksis/core/util/pdf.py:107
+#: aleksis/core/util/pdf.py:115
 msgid "The PDF file has been generated successfully."
 msgstr "Die PDF-Datei wurde erfolgreich generiert."
 
-#: aleksis/core/util/pdf.py:108
+#: aleksis/core/util/pdf.py:116
 msgid "There was a problem while generating the PDF file."
 msgstr "Es ist ein Fehler beim Generieren der PDF-Datei aufgetreten."
 
-#: aleksis/core/util/pdf.py:111
+#: aleksis/core/util/pdf.py:119
 msgid "Download PDF"
 msgstr "PDF herunterladen"
 
-#: aleksis/core/views.py:270
+#: aleksis/core/views.py:280
 msgid "The school term has been created."
 msgstr "Das Schuljahr wurde erstellt."
 
-#: aleksis/core/views.py:282
+#: aleksis/core/views.py:292
 msgid "The school term has been saved."
 msgstr "Das Schuljahr wurde gespeichert."
 
-#: aleksis/core/views.py:406
+#: aleksis/core/views.py:416
 msgid "The child groups were successfully saved."
 msgstr "Die Untergruppen wurden gespeichert."
 
-#: aleksis/core/views.py:425 aleksis/core/views.py:435
+#: aleksis/core/views.py:435 aleksis/core/views.py:445
 msgid "The person has been saved."
 msgstr "Die Person wurde gespeichert."
 
-#: aleksis/core/views.py:485
+#: aleksis/core/views.py:495
 msgid "The group has been saved."
 msgstr "Die Gruppe wurde gespeichert."
 
-#: aleksis/core/views.py:582
+#: aleksis/core/views.py:592
 msgid "The announcement has been saved."
 msgstr "Die Ankündigung wurde gespeichert."
 
-#: aleksis/core/views.py:598
+#: aleksis/core/views.py:608
 msgid "The announcement has been deleted."
 msgstr "Ankündigung wurde gelöscht."
 
-#: aleksis/core/views.py:682
+#: aleksis/core/views.py:695
 msgid "The preferences have been saved successfully."
 msgstr "Die Einstellungen wurde gespeichert."
 
-#: aleksis/core/views.py:706
+#: aleksis/core/views.py:719
 msgid "The person has been deleted."
 msgstr "Die Person wurde gelöscht."
 
-#: aleksis/core/views.py:720
+#: aleksis/core/views.py:733
 msgid "The group has been deleted."
 msgstr "Die Gruppe wurde gelöscht."
 
-#: aleksis/core/views.py:752
+#: aleksis/core/views.py:765
 msgid "The additional_field has been saved."
 msgstr "Das zusätzliche Feld wurde gespeichert."
 
-#: aleksis/core/views.py:786
+#: aleksis/core/views.py:799
 msgid "The additional field has been deleted."
 msgstr "Das zusätzliche Feld wurde gelöscht."
 
-#: aleksis/core/views.py:811
+#: aleksis/core/views.py:824
 msgid "The group type has been saved."
 msgstr "Der Gruppentyp wurde gespeichert."
 
-#: aleksis/core/views.py:841
+#: aleksis/core/views.py:854
 msgid "The group type has been deleted."
 msgstr "Der Gruppentyp wurde gelöscht."
 
-#: aleksis/core/views.py:874
+#: aleksis/core/views.py:887
 msgid "Progress: Run data checks"
 msgstr "Fortschritt: Datenprüfungen ausführen"
 
-#: aleksis/core/views.py:875
+#: aleksis/core/views.py:888
 msgid "Run data checks …"
 msgstr "Datenprüfungen laufen …"
 
-#: aleksis/core/views.py:876
+#: aleksis/core/views.py:889
 msgid "The data checks were run successfully."
 msgstr "Die Datenprüfungen wurden erfolgreich ausgeführt."
 
-#: aleksis/core/views.py:877
+#: aleksis/core/views.py:890
 msgid "There was a problem while running data checks."
 msgstr "Es gab ein Problem beim Ausführen der Datenprüfungen."
 
-#: aleksis/core/views.py:893
+#: aleksis/core/views.py:906
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr "Die Lösungsoption \"{solve_option_obj.verbose_name}\" "
 
-#: aleksis/core/views.py:935
+#: aleksis/core/views.py:948
 msgid "The dashboard widget has been saved."
 msgstr "Das Dashboard-Widget wurde gespeichert."
 
-#: aleksis/core/views.py:965
+#: aleksis/core/views.py:978
 msgid "The dashboard widget has been created."
 msgstr "Das Dashboard-Widget wurde erstellt."
 
-#: aleksis/core/views.py:975
+#: aleksis/core/views.py:988
 msgid "The dashboard widget has been deleted."
 msgstr "Das Dashboard-Widget wurde gelöscht."
 
-#: aleksis/core/views.py:1042
+#: aleksis/core/views.py:1055
 msgid "Your dashboard configuration has been saved successfully."
 msgstr "Ihre Dashboardkonfiguration wurde erfolgreich gespeichert."
 
-#: aleksis/core/views.py:1044
+#: aleksis/core/views.py:1057
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr "Die Konfiguration des Standard-Dashboardes wurde erfolgreich gespeichert."
 
-#: aleksis/core/views.py:1139
+#: aleksis/core/views.py:1127
+#, python-brace-format
+msgid "The invitation was successfully created. The invitation code is {code}"
+msgstr "Die Einladung wurde erfolgreich erstellt. Der Einladungscode ist {code}"
+
+#: aleksis/core/views.py:1218
 msgid "We have successfully assigned the permissions."
 msgstr "Wir haben die Berechtigungen erfolgreich zugewiesen."
 
-#: aleksis/core/views.py:1149
+#: aleksis/core/views.py:1228
 msgid "The global user permission has been deleted."
 msgstr "Die globale Benutzerberechtigung wurde gelöscht."
 
-#: aleksis/core/views.py:1159
+#: aleksis/core/views.py:1238
 msgid "The global group permission has been deleted."
 msgstr "Die globale Gruppenberechtigung wurde gelöscht."
 
-#: aleksis/core/views.py:1169
+#: aleksis/core/views.py:1248
 msgid "The object user permission has been deleted."
 msgstr "Die Objekt-Benutzerberechtigung wurde gelöscht."
 
-#: aleksis/core/views.py:1179
+#: aleksis/core/views.py:1258
 msgid "The object group permission has been deleted."
 msgstr "Die Objekt-Gruppenberechtigung wurde gelöscht."
 
-#: aleksis/core/views.py:1298
+#: aleksis/core/views.py:1377
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr "Das Drittanbieter-Konto konnte nicht deaktiviert werden, weil es die einzige verfügbare Anmeldeoption ist."
 
-#: aleksis/core/views.py:1305
+#: aleksis/core/views.py:1384
 msgid "The third-party account has been successfully disconnected."
 msgstr "Das Drittanbieter-Konto wurde erfolgreich getrennt."
 
+#: aleksis/core/views.py:1441
+msgid "Person was invited successfully."
+msgstr "Person wurde erfolgreich eingeladen."
+
+#: aleksis/core/views.py:1443
+msgid "Person was already invited."
+msgstr "Person wurde bereits eingeladen."
+
+#~ msgid "Consents"
+#~ msgstr "Zustimmungen"
+
+#, python-brace-format
+#~ msgid "I have read the <a href='{privacy_policy}'>Privacy policy</a> and agree with them."
+#~ msgstr "Ich habe die <a href='{privacy_policy}'>Datenschutzerklärung</a> gelesen und stimme ihr zu."
+
+#~ msgid "You must type the same password each time."
+#~ msgstr "Sie müssen zweimal das gleiche Passwort eingeben."
+
+#~ msgid "Impersonation"
+#~ msgstr "Verkleidung"
+
+#, python-format
+#~ msgid "<strong>Note:</strong> you can still <a href=\"%(email_url)s\">change your e-mail address</a>"
+#~ msgstr "<strong>Hinweis:</strong> Sie können immer noch <a href=\"%(email_url)s\"> Ihre E-Mail-Adresse ändern</a>"
+
+#~ msgid "Impress"
+#~ msgstr "Impressum"
+
+#~ msgid "The backup folder doesn't exist."
+#~ msgstr "Der Backup-Ordner existiert nicht."
+
 #~ msgid "Can add oauth applications"
 #~ msgstr "Kann OAuth-Anwendungen hinzufügen"
 
@@ -3281,9 +3388,6 @@ msgstr "Das Drittanbieter-Konto wurde erfolgreich getrennt."
 #~ msgid "New account"
 #~ msgstr "Neues Konto"
 
-#~ msgid "Impersonate user"
-#~ msgstr "Als Benutzer verkleiden"
-
 #~ msgid "Authorized tokens"
 #~ msgstr "Autorisierte Tokens"
 
@@ -3548,9 +3652,6 @@ msgstr "Das Drittanbieter-Konto wurde erfolgreich getrennt."
 #~ msgid "Assorted"
 #~ msgstr "Unsortiert"
 
-#~ msgid "Administration"
-#~ msgstr "Verwaltung"
-
 #~ msgid "List of all groups"
 #~ msgstr "Liste aller Gruppen"
 
diff --git a/aleksis/core/locale/de_DE/LC_MESSAGES/djangojs.po b/aleksis/core/locale/de_DE/LC_MESSAGES/djangojs.po
index f104a1ea290e906c7f0e1d5d37aca398a16241e9..188d883c0746a8cb64414091b5e0fa2f8b247805 100644
--- a/aleksis/core/locale/de_DE/LC_MESSAGES/djangojs.po
+++ b/aleksis/core/locale/de_DE/LC_MESSAGES/djangojs.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-11-29 09:59+0100\n"
+"POT-Creation-Date: 2021-12-28 12:14+0100\n"
 "PO-Revision-Date: 2021-10-28 14:37+0000\n"
 "Last-Translator: Jonathan Weth <teckids@jonathanweth.de>\n"
 "Language-Team: German <https://translate.edugit.org/projects/aleksis/aleksis-core-js/de/>\n"
@@ -30,6 +30,6 @@ msgstr "Abbrechen"
 msgid "OK"
 msgstr "OK"
 
-#: aleksis/core/static/js/main.js:127
+#: aleksis/core/static/js/main.js:128
 msgid "This page may contain outdated information since there is no internet connection."
 msgstr "Diese Seite enthält vielleicht veraltete Informationen, da es keine Internetverbindung gibt."
diff --git a/aleksis/core/locale/fr/LC_MESSAGES/django.po b/aleksis/core/locale/fr/LC_MESSAGES/django.po
index 785f1ea6d42cdd2a00b6718d02a6ea578fe547fe..48ad0ea73dda60181ae19660fa021259b0f49ab0 100644
--- a/aleksis/core/locale/fr/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/fr/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: AlekSIS (School Information System) 0.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-11-29 09:59+0100\n"
+"POT-Creation-Date: 2021-12-28 12:13+0100\n"
 "PO-Revision-Date: 2021-06-16 12:00+0000\n"
 "Last-Translator: Jonathan Weth <teckids@jonathanweth.de>\n"
 "Language-Team: French <https://translate.edugit.org/projects/aleksis/aleksis/fr/>\n"
@@ -18,28 +18,37 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=n > 1;\n"
 "X-Generator: Weblate 4.4\n"
 
-#: aleksis/core/apps.py:151
+#: aleksis/core/apps.py:152
 msgid "OpenID Connect scope"
 msgstr ""
 
-#: aleksis/core/apps.py:152
+#: aleksis/core/apps.py:153
 msgid "Given name, family name, link to profile and picture if existing."
 msgstr ""
 
-#: aleksis/core/apps.py:153
+#: aleksis/core/apps.py:154
 msgid "Full home postal address"
 msgstr ""
 
-#: aleksis/core/apps.py:154
+#: aleksis/core/apps.py:155
 #, fuzzy
 #| msgid "Contact details"
 msgid "Email address"
 msgstr "Détails de contact"
 
-#: aleksis/core/apps.py:155
+#: aleksis/core/apps.py:156
 msgid "Home and mobile phone"
 msgstr ""
 
+#: aleksis/core/apps.py:157 aleksis/core/forms.py:223 aleksis/core/menus.py:265
+#: aleksis/core/models.py:414 aleksis/core/templates/core/group/list.html:8
+#: aleksis/core/templates/core/group/list.html:9
+#: aleksis/core/templates/core/person/full.html:152
+#, fuzzy
+#| msgid "Group"
+msgid "Groups"
+msgstr "Groupe"
+
 #: aleksis/core/data_checks.py:55
 msgid "Ignore problem"
 msgstr ""
@@ -88,62 +97,62 @@ msgstr ""
 msgid "Content type"
 msgstr ""
 
-#: aleksis/core/filters.py:112 aleksis/core/models.py:579
+#: aleksis/core/filters.py:112 aleksis/core/models.py:581
 msgid "User"
 msgstr ""
 
-#: aleksis/core/filters.py:134 aleksis/core/models.py:411
+#: aleksis/core/filters.py:134 aleksis/core/models.py:413
 msgid "Group"
 msgstr "groupe"
 
-#: aleksis/core/forms.py:46 aleksis/core/forms.py:514
+#: aleksis/core/forms.py:48 aleksis/core/forms.py:559
 msgid "Base data"
 msgstr ""
 
-#: aleksis/core/forms.py:52
+#: aleksis/core/forms.py:54
 msgid "Address"
 msgstr ""
 
-#: aleksis/core/forms.py:53
+#: aleksis/core/forms.py:55 aleksis/core/forms.py:568
 #, fuzzy
 #| msgid "Contact details"
 msgid "Contact data"
 msgstr "Détails de contact"
 
-#: aleksis/core/forms.py:55
+#: aleksis/core/forms.py:57
 #, fuzzy
 #| msgid "Contact details"
 msgid "Advanced personal data"
 msgstr "Détails de contact"
 
-#: aleksis/core/forms.py:103
+#: aleksis/core/forms.py:105
 msgid "New user"
 msgstr ""
 
-#: aleksis/core/forms.py:103
+#: aleksis/core/forms.py:105
 msgid "Create a new account"
 msgstr ""
 
-#: aleksis/core/forms.py:134
+#: aleksis/core/forms.py:136
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr ""
 
-#: aleksis/core/forms.py:138
+#: aleksis/core/forms.py:140
 msgid "This username is already in use."
 msgstr "Cet nom est deja en utilisation."
 
-#: aleksis/core/forms.py:155 aleksis/core/models.py:125
+#: aleksis/core/forms.py:157 aleksis/core/models.py:129
 msgid "School term"
 msgstr ""
 
-#: aleksis/core/forms.py:156
+#: aleksis/core/forms.py:158
 #, fuzzy
 #| msgid "Contact details"
 msgid "Common data"
 msgstr "Détails de contact"
 
-#: aleksis/core/forms.py:157 aleksis/core/forms.py:208
-#: aleksis/core/menus.py:256 aleksis/core/models.py:148
+#: aleksis/core/forms.py:159 aleksis/core/forms.py:210
+#: aleksis/core/menus.py:254 aleksis/core/models.py:152
 #: aleksis/core/templates/core/person/list.html:8
 #: aleksis/core/templates/core/person/list.html:9
 #, fuzzy
@@ -151,107 +160,97 @@ msgstr "Détails de contact"
 msgid "Persons"
 msgstr "Personne"
 
-#: aleksis/core/forms.py:158
+#: aleksis/core/forms.py:160 aleksis/core/forms.py:570
 #, fuzzy
 #| msgid "Contact details"
 msgid "Additional data"
 msgstr "Détails de contact"
 
-#: aleksis/core/forms.py:200 aleksis/core/forms.py:203
-#: aleksis/core/models.py:68
+#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
+#: aleksis/core/models.py:72
 msgid "Date"
 msgstr "Date"
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:76
+#: aleksis/core/forms.py:203 aleksis/core/forms.py:206
+#: aleksis/core/models.py:80
 msgid "Time"
 msgstr ""
 
-#: aleksis/core/forms.py:221 aleksis/core/menus.py:267
-#: aleksis/core/models.py:412 aleksis/core/templates/core/group/list.html:8
-#: aleksis/core/templates/core/group/list.html:9
-#: aleksis/core/templates/core/person/full.html:145
-#, fuzzy
-#| msgid "Group"
-msgid "Groups"
-msgstr "Groupe"
-
-#: aleksis/core/forms.py:234
+#: aleksis/core/forms.py:236
 msgid "From when until when should the announcement be displayed?"
 msgstr ""
 
-#: aleksis/core/forms.py:237
+#: aleksis/core/forms.py:239
 msgid "Who should see the announcement?"
 msgstr ""
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:240
 msgid "Write your announcement:"
 msgstr ""
 
-#: aleksis/core/forms.py:277
+#: aleksis/core/forms.py:279
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr ""
 
-#: aleksis/core/forms.py:281
+#: aleksis/core/forms.py:283
 msgid "The from date and time must be earlier then the until date and time."
 msgstr ""
 
-#: aleksis/core/forms.py:290
+#: aleksis/core/forms.py:292
 msgid "You need at least one recipient."
 msgstr ""
 
-#: aleksis/core/forms.py:411
+#: aleksis/core/forms.py:401
+msgid "Invitation code"
+msgstr ""
+
+#: aleksis/core/forms.py:402
+msgid "Please enter your invitation code."
+msgstr ""
+
+#: aleksis/core/forms.py:434
 msgid "Who should get the permission?"
 msgstr ""
 
-#: aleksis/core/forms.py:412
+#: aleksis/core/forms.py:435
 msgid "On what?"
 msgstr ""
 
-#: aleksis/core/forms.py:438
+#: aleksis/core/forms.py:461
 msgid "Select objects which the permission should be granted for:"
 msgstr ""
 
-#: aleksis/core/forms.py:441
+#: aleksis/core/forms.py:464
 msgid "Grant the permission for all objects"
 msgstr ""
 
-#: aleksis/core/forms.py:449
+#: aleksis/core/forms.py:472
 msgid "You must select at least one group or person which should get the permission."
 msgstr ""
 
-#: aleksis/core/forms.py:454
+#: aleksis/core/forms.py:477
 msgid "You must grant the permission to all objects and/or to some objects."
 msgstr ""
 
-#: aleksis/core/forms.py:518
+#: aleksis/core/forms.py:564
+msgid "Adress data"
+msgstr ""
+
+#: aleksis/core/forms.py:576
 #, fuzzy
 #| msgid "Contact details"
 msgid "Account data"
 msgstr "Détails de contact"
 
-#: aleksis/core/forms.py:524
-msgid "Consents"
-msgstr ""
-
-#: aleksis/core/forms.py:531
+#: aleksis/core/forms.py:583
 msgid "Password"
 msgstr ""
 
-#: aleksis/core/forms.py:537
+#: aleksis/core/forms.py:586
 msgid "Password (again)"
 msgstr ""
 
-#: aleksis/core/forms.py:550
-#, python-brace-format
-msgid "I have read the <a href='{privacy_policy}'>Privacy policy</a> and agree with them."
-msgstr ""
-
-#: aleksis/core/forms.py:575
-msgid "You must type the same password each time."
-msgstr ""
-
-#: aleksis/core/forms.py:720
+#: aleksis/core/forms.py:752
 msgid "No valid selection."
 msgstr ""
 
@@ -259,24 +258,20 @@ msgstr ""
 msgid "There are unresolved data problems."
 msgstr ""
 
-#: aleksis/core/health_checks.py:38
-msgid "The backup folder doesn't exist."
-msgstr ""
-
-#: aleksis/core/health_checks.py:47
+#: aleksis/core/health_checks.py:44
 #, python-brace-format
 msgid "Last backup {time_gone_since_backup}!"
 msgstr ""
 
-#: aleksis/core/health_checks.py:49
+#: aleksis/core/health_checks.py:46
 msgid "No backup found!"
 msgstr ""
 
-#: aleksis/core/health_checks.py:76
+#: aleksis/core/health_checks.py:73
 msgid "No backup result found!"
 msgstr ""
 
-#: aleksis/core/health_checks.py:78
+#: aleksis/core/health_checks.py:75
 #, python-brace-format
 msgid "{task.status} - {task.result}"
 msgstr ""
@@ -292,34 +287,38 @@ msgstr ""
 msgid "Sign up"
 msgstr ""
 
-#: aleksis/core/menus.py:24
+#: aleksis/core/menus.py:24 aleksis/core/templates/invitations/enter.html:7
+msgid "Accept invitation"
+msgstr ""
+
+#: aleksis/core/menus.py:33
 msgid "Dashboard"
 msgstr ""
 
-#: aleksis/core/menus.py:32 aleksis/core/models.py:625
-#: aleksis/core/preferences.py:27
+#: aleksis/core/menus.py:41 aleksis/core/models.py:627
+#: aleksis/core/preferences.py:28
 #: aleksis/core/templates/core/notifications.html:4
 #: aleksis/core/templates/core/notifications.html:5
 msgid "Notifications"
 msgstr ""
 
-#: aleksis/core/menus.py:44
+#: aleksis/core/menus.py:53
 msgid "Account"
 msgstr ""
 
-#: aleksis/core/menus.py:51
+#: aleksis/core/menus.py:60
 msgid "Stop impersonation"
 msgstr ""
 
-#: aleksis/core/menus.py:60 aleksis/core/templates/core/base.html:80
+#: aleksis/core/menus.py:69 aleksis/core/templates/core/base.html:80
 msgid "Logout"
 msgstr ""
 
-#: aleksis/core/menus.py:66
+#: aleksis/core/menus.py:75
 msgid "2FA"
 msgstr ""
 
-#: aleksis/core/menus.py:74
+#: aleksis/core/menus.py:83
 #: aleksis/core/templates/account/password_change.html:5
 #: aleksis/core/templates/account/password_change.html:6
 #: aleksis/core/templates/account/password_change.html:19
@@ -331,92 +330,88 @@ msgstr ""
 msgid "Change password"
 msgstr ""
 
-#: aleksis/core/menus.py:86
+#: aleksis/core/menus.py:95
 msgid "Me"
 msgstr ""
 
-#: aleksis/core/menus.py:95
+#: aleksis/core/menus.py:104
 #: aleksis/core/templates/dynamic_preferences/form.html:5
 msgid "Preferences"
 msgstr ""
 
-#: aleksis/core/menus.py:104
+#: aleksis/core/menus.py:113
 msgid "Third-party accounts"
 msgstr ""
 
-#: aleksis/core/menus.py:113
+#: aleksis/core/menus.py:122
 #: aleksis/core/templates/oauth2_provider/authorized-tokens.html:5
 #: aleksis/core/templates/oauth2_provider/authorized-tokens.html:6
 msgid "Authorized applications"
 msgstr ""
 
-#: aleksis/core/menus.py:124
+#: aleksis/core/menus.py:133
 msgid "Admin"
 msgstr ""
 
-#: aleksis/core/menus.py:132 aleksis/core/models.py:725
+#: aleksis/core/menus.py:141 aleksis/core/models.py:727
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr ""
 
-#: aleksis/core/menus.py:143 aleksis/core/models.py:126
+#: aleksis/core/menus.py:152 aleksis/core/models.py:130
 #: aleksis/core/templates/core/school_term/list.html:8
 #: aleksis/core/templates/core/school_term/list.html:9
 msgid "School terms"
 msgstr ""
 
-#: aleksis/core/menus.py:154
+#: aleksis/core/menus.py:163
 #: aleksis/core/templates/core/dashboard_widget/list.html:8
 #: aleksis/core/templates/core/dashboard_widget/list.html:9
 msgid "Dashboard widgets"
 msgstr ""
 
-#: aleksis/core/menus.py:165
+#: aleksis/core/menus.py:174
 #: aleksis/core/templates/core/management/data_management.html:6
 #: aleksis/core/templates/core/management/data_management.html:7
 msgid "Data management"
 msgstr ""
 
-#: aleksis/core/menus.py:176
+#: aleksis/core/menus.py:185
 #: aleksis/core/templates/core/pages/system_status.html:5
 #: aleksis/core/templates/core/pages/system_status.html:7
 msgid "System status"
 msgstr ""
 
-#: aleksis/core/menus.py:187
-msgid "Impersonation"
-msgstr ""
-
-#: aleksis/core/menus.py:198
+#: aleksis/core/menus.py:196
 msgid "Configuration"
 msgstr ""
 
-#: aleksis/core/menus.py:209 aleksis/core/templates/core/data_check/list.html:9
+#: aleksis/core/menus.py:207 aleksis/core/templates/core/data_check/list.html:9
 #: aleksis/core/templates/core/data_check/list.html:10
 msgid "Data checks"
 msgstr ""
 
-#: aleksis/core/menus.py:215 aleksis/core/templates/core/perms/list.html:13
+#: aleksis/core/menus.py:213 aleksis/core/templates/core/perms/list.html:13
 #: aleksis/core/templates/core/perms/list.html:14
 msgid "Manage permissions"
 msgstr ""
 
-#: aleksis/core/menus.py:226
+#: aleksis/core/menus.py:224
 msgid "Backend Admin"
 msgstr ""
 
-#: aleksis/core/menus.py:234
+#: aleksis/core/menus.py:232
 #: aleksis/core/templates/oauth2_provider/application/list.html:5
 #: aleksis/core/templates/oauth2_provider/application/list.html:6
 msgid "OAuth2 Applications"
 msgstr ""
 
-#: aleksis/core/menus.py:247
+#: aleksis/core/menus.py:245
 msgid "People"
 msgstr ""
 
-#: aleksis/core/menus.py:278 aleksis/core/models.py:979
+#: aleksis/core/menus.py:276 aleksis/core/models.py:981
 #: aleksis/core/templates/core/group_type/list.html:8
 #: aleksis/core/templates/core/group_type/list.html:9
 #, fuzzy
@@ -424,775 +419,805 @@ msgstr ""
 msgid "Group types"
 msgstr "Groupe"
 
-#: aleksis/core/menus.py:289
+#: aleksis/core/menus.py:287
 msgid "Groups and child groups"
 msgstr ""
 
-#: aleksis/core/menus.py:300 aleksis/core/models.py:460
+#: aleksis/core/menus.py:298 aleksis/core/models.py:462
 #: aleksis/core/templates/core/additional_field/list.html:8
 #: aleksis/core/templates/core/additional_field/list.html:9
 msgid "Additional fields"
 msgstr ""
 
-#: aleksis/core/menus.py:315
+#: aleksis/core/menus.py:309
+#, fuzzy
+#| msgid "Contact details"
+msgid "Invite person"
+msgstr "Détails de contact"
+
+#: aleksis/core/menus.py:322
 #: aleksis/core/templates/core/group/child_groups.html:7
 #: aleksis/core/templates/core/group/child_groups.html:9
 msgid "Assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/mixins.py:508
+#: aleksis/core/mixins.py:511
 msgid "Linked school term"
 msgstr ""
 
-#: aleksis/core/models.py:66
+#: aleksis/core/models.py:70
 msgid "Boolean (Yes/No)"
 msgstr ""
 
-#: aleksis/core/models.py:67
+#: aleksis/core/models.py:71
 msgid "Text (one line)"
 msgstr ""
 
-#: aleksis/core/models.py:69
+#: aleksis/core/models.py:73
 msgid "Date and time"
 msgstr ""
 
-#: aleksis/core/models.py:70
+#: aleksis/core/models.py:74
 msgid "Decimal number"
 msgstr ""
 
-#: aleksis/core/models.py:71 aleksis/core/models.py:194
+#: aleksis/core/models.py:75 aleksis/core/models.py:198
 msgid "E-mail address"
 msgstr ""
 
-#: aleksis/core/models.py:72
+#: aleksis/core/models.py:76
 msgid "Integer"
 msgstr ""
 
-#: aleksis/core/models.py:73
+#: aleksis/core/models.py:77
 msgid "IP address"
 msgstr ""
 
-#: aleksis/core/models.py:74
+#: aleksis/core/models.py:78
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr ""
 
-#: aleksis/core/models.py:75
+#: aleksis/core/models.py:79
 msgid "Text (multi-line)"
 msgstr ""
 
-#: aleksis/core/models.py:77
+#: aleksis/core/models.py:81
 msgid "URL / Link"
 msgstr ""
 
-#: aleksis/core/models.py:89 aleksis/core/models.py:948
+#: aleksis/core/models.py:93 aleksis/core/models.py:950
 msgid "Name"
 msgstr ""
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:95
 #, fuzzy
 #| msgid "Contact details"
 msgid "Start date"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:96
 msgid "End date"
 msgstr ""
 
-#: aleksis/core/models.py:111
+#: aleksis/core/models.py:115
 msgid "The start date must be earlier than the end date."
 msgstr ""
 
-#: aleksis/core/models.py:118
+#: aleksis/core/models.py:122
 msgid "There is already a school term for this time or a part of this time."
 msgstr ""
 
-#: aleksis/core/models.py:147 aleksis/core/models.py:897
+#: aleksis/core/models.py:151 aleksis/core/models.py:899
 msgid "Person"
 msgstr "Personne"
 
-#: aleksis/core/models.py:150
+#: aleksis/core/models.py:154
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view address"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:151
+#: aleksis/core/models.py:155
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view contact details"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:152
+#: aleksis/core/models.py:156
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view photo"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:153
+#: aleksis/core/models.py:157
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view persons groups"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:154
+#: aleksis/core/models.py:158
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view personal details"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:164
+#: aleksis/core/models.py:168
 msgid "female"
 msgstr ""
 
-#: aleksis/core/models.py:164
+#: aleksis/core/models.py:168
 msgid "male"
 msgstr ""
 
-#: aleksis/core/models.py:172
+#: aleksis/core/models.py:176 aleksis/core/models.py:1144
 msgid "Linked user"
 msgstr ""
 
-#: aleksis/core/models.py:174
+#: aleksis/core/models.py:178
 msgid "Is person active?"
 msgstr ""
 
-#: aleksis/core/models.py:176
+#: aleksis/core/models.py:180
 msgid "First name"
 msgstr "Prénom"
 
-#: aleksis/core/models.py:177
+#: aleksis/core/models.py:181
 msgid "Last name"
 msgstr "Nom de famille"
 
-#: aleksis/core/models.py:179
+#: aleksis/core/models.py:183
 msgid "Additional name(s)"
 msgstr ""
 
-#: aleksis/core/models.py:183 aleksis/core/models.py:429
+#: aleksis/core/models.py:187 aleksis/core/models.py:431
 #, fuzzy
 #| msgid "First name"
 msgid "Short name"
 msgstr "Prénom"
 
-#: aleksis/core/models.py:186
+#: aleksis/core/models.py:190
 msgid "Street"
 msgstr ""
 
-#: aleksis/core/models.py:187
+#: aleksis/core/models.py:191
 msgid "Street number"
 msgstr ""
 
-#: aleksis/core/models.py:188
+#: aleksis/core/models.py:192
 msgid "Postal code"
 msgstr ""
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:193
 msgid "Place"
 msgstr ""
 
-#: aleksis/core/models.py:191
+#: aleksis/core/models.py:195
 msgid "Home phone"
 msgstr ""
 
-#: aleksis/core/models.py:192
+#: aleksis/core/models.py:196
 msgid "Mobile phone"
 msgstr ""
 
-#: aleksis/core/models.py:196
+#: aleksis/core/models.py:200
 msgid "Date of birth"
 msgstr "Date d'anniversaire"
 
-#: aleksis/core/models.py:198
+#: aleksis/core/models.py:201
 #, fuzzy
 #| msgid "Date of birth"
 msgid "Place of birth"
 msgstr "Date d'anniversaire"
 
-#: aleksis/core/models.py:200
+#: aleksis/core/models.py:202
 msgid "Sex"
 msgstr "Sexe"
 
-#: aleksis/core/models.py:202
+#: aleksis/core/models.py:204
 msgid "Photo"
 msgstr ""
 
-#: aleksis/core/models.py:206 aleksis/core/templates/core/person/full.html:138
+#: aleksis/core/models.py:208 aleksis/core/templates/core/person/full.html:145
 msgid "Guardians / Parents"
 msgstr ""
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:215
 msgid "Primary group"
 msgstr ""
 
-#: aleksis/core/models.py:216 aleksis/core/models.py:583
-#: aleksis/core/models.py:607 aleksis/core/models.py:692
-#: aleksis/core/models.py:972 aleksis/core/templates/core/person/full.html:121
+#: aleksis/core/models.py:218 aleksis/core/models.py:585
+#: aleksis/core/models.py:609 aleksis/core/models.py:694
+#: aleksis/core/models.py:974 aleksis/core/templates/core/person/full.html:128
 msgid "Description"
 msgstr "Description"
 
-#: aleksis/core/models.py:384
+#: aleksis/core/models.py:386
 msgid "Title of field"
 msgstr ""
 
-#: aleksis/core/models.py:386
+#: aleksis/core/models.py:388
 msgid "Type of field"
 msgstr ""
 
-#: aleksis/core/models.py:393
+#: aleksis/core/models.py:395
 msgid "Addtitional field for groups"
 msgstr ""
 
-#: aleksis/core/models.py:394
+#: aleksis/core/models.py:396
 msgid "Addtitional fields for groups"
 msgstr ""
 
-#: aleksis/core/models.py:414
+#: aleksis/core/models.py:416
 msgid "Can assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/models.py:415
+#: aleksis/core/models.py:417
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view statistics about group."
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:427
+#: aleksis/core/models.py:429
 #, fuzzy
 #| msgid "Last name"
 msgid "Long name"
 msgstr "Nom de famille"
 
-#: aleksis/core/models.py:437 aleksis/core/templates/core/group/full.html:85
+#: aleksis/core/models.py:439 aleksis/core/templates/core/group/full.html:85
 msgid "Members"
 msgstr ""
 
-#: aleksis/core/models.py:440 aleksis/core/templates/core/group/full.html:82
+#: aleksis/core/models.py:442 aleksis/core/templates/core/group/full.html:82
 msgid "Owners"
 msgstr "Propriétaires"
 
-#: aleksis/core/models.py:447 aleksis/core/templates/core/group/full.html:55
+#: aleksis/core/models.py:449 aleksis/core/templates/core/group/full.html:55
 msgid "Parent groups"
 msgstr ""
 
-#: aleksis/core/models.py:455
+#: aleksis/core/models.py:457
 msgid "Type of group"
 msgstr ""
 
-#: aleksis/core/models.py:582 aleksis/core/models.py:606
-#: aleksis/core/models.py:691
+#: aleksis/core/models.py:584 aleksis/core/models.py:608
+#: aleksis/core/models.py:693
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr ""
 
-#: aleksis/core/models.py:585
+#: aleksis/core/models.py:587
 msgid "Application"
 msgstr ""
 
-#: aleksis/core/models.py:591
+#: aleksis/core/models.py:593
 msgid "Activity"
 msgstr ""
 
-#: aleksis/core/models.py:592
+#: aleksis/core/models.py:594
 msgid "Activities"
 msgstr ""
 
-#: aleksis/core/models.py:598
+#: aleksis/core/models.py:600
 msgid "Sender"
 msgstr ""
 
-#: aleksis/core/models.py:603
+#: aleksis/core/models.py:605
 msgid "Recipient"
 msgstr ""
 
-#: aleksis/core/models.py:608 aleksis/core/models.py:949
+#: aleksis/core/models.py:610 aleksis/core/models.py:951
 msgid "Link"
 msgstr ""
 
-#: aleksis/core/models.py:610
+#: aleksis/core/models.py:612
 msgid "Read"
 msgstr ""
 
-#: aleksis/core/models.py:611
+#: aleksis/core/models.py:613
 msgid "Sent"
 msgstr ""
 
-#: aleksis/core/models.py:624
+#: aleksis/core/models.py:626
 msgid "Notification"
 msgstr ""
 
-#: aleksis/core/models.py:693
+#: aleksis/core/models.py:695
 msgid "Link to detailed view"
 msgstr ""
 
-#: aleksis/core/models.py:696
+#: aleksis/core/models.py:698
 msgid "Date and time from when to show"
 msgstr ""
 
-#: aleksis/core/models.py:699
+#: aleksis/core/models.py:701
 msgid "Date and time until when to show"
 msgstr ""
 
-#: aleksis/core/models.py:724
+#: aleksis/core/models.py:726
 msgid "Announcement"
 msgstr ""
 
-#: aleksis/core/models.py:762
+#: aleksis/core/models.py:764
 msgid "Announcement recipient"
 msgstr ""
 
-#: aleksis/core/models.py:763
+#: aleksis/core/models.py:765
 msgid "Announcement recipients"
 msgstr ""
 
-#: aleksis/core/models.py:818
+#: aleksis/core/models.py:820
 msgid "Widget Title"
 msgstr ""
 
-#: aleksis/core/models.py:819
+#: aleksis/core/models.py:821
 msgid "Activate Widget"
 msgstr ""
 
-#: aleksis/core/models.py:820
+#: aleksis/core/models.py:822
 msgid "Widget is broken"
 msgstr ""
 
-#: aleksis/core/models.py:823
+#: aleksis/core/models.py:825
 msgid "Size on mobile devices"
 msgstr ""
 
-#: aleksis/core/models.py:824
+#: aleksis/core/models.py:826
 msgid "<= 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:829
+#: aleksis/core/models.py:831
 msgid "Size on tablet devices"
 msgstr ""
 
-#: aleksis/core/models.py:830
+#: aleksis/core/models.py:832
 msgid "> 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:835
+#: aleksis/core/models.py:837
 msgid "Size on desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:836
+#: aleksis/core/models.py:838
 msgid "> 992 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:841
+#: aleksis/core/models.py:843
 msgid "Size on large desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:842
+#: aleksis/core/models.py:844
 msgid "> 1200 px>, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:873
+#: aleksis/core/models.py:875
 msgid "Can edit default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:874
+#: aleksis/core/models.py:876
 msgid "Dashboard Widget"
 msgstr ""
 
-#: aleksis/core/models.py:875
+#: aleksis/core/models.py:877
 msgid "Dashboard Widgets"
 msgstr ""
 
-#: aleksis/core/models.py:881
+#: aleksis/core/models.py:883
 msgid "URL"
 msgstr ""
 
-#: aleksis/core/models.py:882
+#: aleksis/core/models.py:884
 msgid "Icon URL"
 msgstr ""
 
-#: aleksis/core/models.py:888
+#: aleksis/core/models.py:890
 msgid "External link widget"
 msgstr ""
 
-#: aleksis/core/models.py:889
+#: aleksis/core/models.py:891
 msgid "External link widgets"
 msgstr ""
 
-#: aleksis/core/models.py:894
+#: aleksis/core/models.py:896
 msgid "Dashboard widget"
 msgstr ""
 
-#: aleksis/core/models.py:899
+#: aleksis/core/models.py:901
 msgid "Order"
 msgstr ""
 
-#: aleksis/core/models.py:900
+#: aleksis/core/models.py:902
 msgid "Part of the default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:915
+#: aleksis/core/models.py:917
 msgid "Dashboard widget order"
 msgstr ""
 
-#: aleksis/core/models.py:916
+#: aleksis/core/models.py:918
 msgid "Dashboard widget orders"
 msgstr ""
 
-#: aleksis/core/models.py:922
+#: aleksis/core/models.py:924
 msgid "Menu ID"
 msgstr ""
 
-#: aleksis/core/models.py:935
+#: aleksis/core/models.py:937
 msgid "Custom menu"
 msgstr ""
 
-#: aleksis/core/models.py:936
+#: aleksis/core/models.py:938
 msgid "Custom menus"
 msgstr ""
 
-#: aleksis/core/models.py:946
+#: aleksis/core/models.py:948
 msgid "Menu"
 msgstr ""
 
-#: aleksis/core/models.py:950
+#: aleksis/core/models.py:952
 msgid "Icon"
 msgstr ""
 
-#: aleksis/core/models.py:956
+#: aleksis/core/models.py:958
 msgid "Custom menu item"
 msgstr ""
 
-#: aleksis/core/models.py:957
+#: aleksis/core/models.py:959
 msgid "Custom menu items"
 msgstr ""
 
-#: aleksis/core/models.py:971
+#: aleksis/core/models.py:973
 msgid "Title of type"
 msgstr ""
 
-#: aleksis/core/models.py:978 aleksis/core/templates/core/group/full.html:47
+#: aleksis/core/models.py:980 aleksis/core/templates/core/group/full.html:47
 #, fuzzy
 #| msgid "Group"
 msgid "Group type"
 msgstr "Groupe"
 
-#: aleksis/core/models.py:992
+#: aleksis/core/models.py:994
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can view system status"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:993
+#: aleksis/core/models.py:995
 msgid "Can manage data"
 msgstr ""
 
-#: aleksis/core/models.py:994
+#: aleksis/core/models.py:996
 #, fuzzy
 #| msgid "Contact details"
 msgid "Can impersonate"
 msgstr "Détails de contact"
 
-#: aleksis/core/models.py:995
+#: aleksis/core/models.py:997
 msgid "Can use search"
 msgstr ""
 
-#: aleksis/core/models.py:996
+#: aleksis/core/models.py:998
 msgid "Can change site preferences"
 msgstr ""
 
-#: aleksis/core/models.py:997
+#: aleksis/core/models.py:999
 msgid "Can change person preferences"
 msgstr ""
 
-#: aleksis/core/models.py:998
+#: aleksis/core/models.py:1000
 msgid "Can change group preferences"
 msgstr ""
 
-#: aleksis/core/models.py:999
+#: aleksis/core/models.py:1001
 msgid "Can test PDF generation"
 msgstr ""
 
-#: aleksis/core/models.py:1035
+#: aleksis/core/models.py:1037
 msgid "Related data check task"
 msgstr ""
 
-#: aleksis/core/models.py:1043
+#: aleksis/core/models.py:1045
 msgid "Issue solved"
 msgstr ""
 
-#: aleksis/core/models.py:1044
+#: aleksis/core/models.py:1046
 msgid "Notification sent"
 msgstr ""
 
-#: aleksis/core/models.py:1057
+#: aleksis/core/models.py:1059
 msgid "Data check result"
 msgstr ""
 
-#: aleksis/core/models.py:1058
+#: aleksis/core/models.py:1060
 msgid "Data check results"
 msgstr ""
 
-#: aleksis/core/models.py:1060
+#: aleksis/core/models.py:1062
 msgid "Can run data checks"
 msgstr ""
 
-#: aleksis/core/models.py:1061
+#: aleksis/core/models.py:1063
 msgid "Can solve data check problems"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1070
+#, fuzzy
+#| msgid "Contact details"
+msgid "E-Mail address"
+msgstr "Détails de contact"
+
+#: aleksis/core/models.py:1094
 #, fuzzy
 #| msgid "Owners"
 msgid "Owner"
 msgstr "Propriétaires"
 
-#: aleksis/core/models.py:1080
+#: aleksis/core/models.py:1098
 msgid "File expires at"
 msgstr ""
 
-#: aleksis/core/models.py:1082
+#: aleksis/core/models.py:1100
 msgid "Generated HTML file"
 msgstr ""
 
-#: aleksis/core/models.py:1084
+#: aleksis/core/models.py:1102
 msgid "Generated PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1091
+#: aleksis/core/models.py:1109
 msgid "PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1092
+#: aleksis/core/models.py:1110
 msgid "PDF files"
 msgstr ""
 
-#: aleksis/core/models.py:1097
+#: aleksis/core/models.py:1115
 msgid "Task result"
 msgstr ""
 
-#: aleksis/core/models.py:1100
+#: aleksis/core/models.py:1118
 msgid "Task user"
 msgstr ""
 
-#: aleksis/core/models.py:1112
+#: aleksis/core/models.py:1130
 msgid "Task user assignment"
 msgstr ""
 
-#: aleksis/core/models.py:1113
+#: aleksis/core/models.py:1131
 msgid "Task user assignments"
 msgstr ""
 
-#: aleksis/core/models.py:1127
+#: aleksis/core/models.py:1147
+#, fuzzy
+#| msgid "Contact details"
+msgid "Additional attributes"
+msgstr "Détails de contact"
+
+#: aleksis/core/models.py:1185
 msgid "Allowed scopes that clients can request"
 msgstr ""
 
-#: aleksis/core/preferences.py:23
+#: aleksis/core/preferences.py:24
 msgid "General"
 msgstr ""
 
-#: aleksis/core/preferences.py:24
+#: aleksis/core/preferences.py:25
 msgid "School"
 msgstr ""
 
-#: aleksis/core/preferences.py:25
+#: aleksis/core/preferences.py:26
 msgid "Theme"
 msgstr ""
 
-#: aleksis/core/preferences.py:26
+#: aleksis/core/preferences.py:27
 msgid "Mail"
 msgstr ""
 
-#: aleksis/core/preferences.py:28
+#: aleksis/core/preferences.py:29
 msgid "Footer"
 msgstr ""
 
-#: aleksis/core/preferences.py:29
+#: aleksis/core/preferences.py:30
 #, fuzzy
 #| msgid "Contact details"
 msgid "Accounts"
 msgstr "Détails de contact"
 
-#: aleksis/core/preferences.py:30
+#: aleksis/core/preferences.py:31
 msgid "Authentication"
 msgstr ""
 
-#: aleksis/core/preferences.py:31
+#: aleksis/core/preferences.py:32
 msgid "Internationalisation"
 msgstr ""
 
-#: aleksis/core/preferences.py:42
+#: aleksis/core/preferences.py:43
 msgid "Site title"
 msgstr ""
 
-#: aleksis/core/preferences.py:53
+#: aleksis/core/preferences.py:54
 #, fuzzy
 #| msgid "Description"
 msgid "Site description"
 msgstr "Description"
 
-#: aleksis/core/preferences.py:64
+#: aleksis/core/preferences.py:65
 msgid "Primary colour"
 msgstr ""
 
-#: aleksis/core/preferences.py:76
+#: aleksis/core/preferences.py:77
 msgid "Secondary colour"
 msgstr ""
 
-#: aleksis/core/preferences.py:87
+#: aleksis/core/preferences.py:88
 msgid "Logo"
 msgstr ""
 
-#: aleksis/core/preferences.py:97
+#: aleksis/core/preferences.py:98
 msgid "Favicon"
 msgstr ""
 
-#: aleksis/core/preferences.py:107
+#: aleksis/core/preferences.py:108
 msgid "PWA-Icon"
 msgstr ""
 
-#: aleksis/core/preferences.py:118
+#: aleksis/core/preferences.py:119
 #, fuzzy
 #| msgid "Last name"
 msgid "Mail out name"
 msgstr "Nom de famille"
 
-#: aleksis/core/preferences.py:129
+#: aleksis/core/preferences.py:130
 msgid "Mail out address"
 msgstr ""
 
-#: aleksis/core/preferences.py:141
+#: aleksis/core/preferences.py:142
 msgid "Link to privacy policy"
 msgstr ""
 
-#: aleksis/core/preferences.py:153
+#: aleksis/core/preferences.py:154
 msgid "Link to imprint"
 msgstr ""
 
-#: aleksis/core/preferences.py:165
+#: aleksis/core/preferences.py:166
 msgid "Name format for addressing"
 msgstr ""
 
-#: aleksis/core/preferences.py:181
+#: aleksis/core/preferences.py:182
 msgid "Channels to use for notifications"
 msgstr ""
 
-#: aleksis/core/preferences.py:193
+#: aleksis/core/preferences.py:194
 msgid "Regular expression to match primary group, e.g. '^Class .*'"
 msgstr ""
 
-#: aleksis/core/preferences.py:204
+#: aleksis/core/preferences.py:205
 msgid "Field on person to match primary group against"
 msgstr ""
 
-#: aleksis/core/preferences.py:216
+#: aleksis/core/preferences.py:217
 msgid "Automatically create new persons for new users"
 msgstr ""
 
-#: aleksis/core/preferences.py:225
+#: aleksis/core/preferences.py:226
 msgid "Automatically link existing persons to new users by their e-mail address"
 msgstr ""
 
-#: aleksis/core/preferences.py:236
+#: aleksis/core/preferences.py:237
 msgid "Display name of the school"
 msgstr ""
 
-#: aleksis/core/preferences.py:247
+#: aleksis/core/preferences.py:248
 msgid "Official name of the school, e.g. as given by supervisory authority"
 msgstr ""
 
-#: aleksis/core/preferences.py:255
+#: aleksis/core/preferences.py:256
 msgid "Allow users to change their passwords"
 msgstr ""
 
-#: aleksis/core/preferences.py:263
+#: aleksis/core/preferences.py:264
 msgid "Enable signup"
 msgstr ""
 
-#: aleksis/core/preferences.py:274
+#: aleksis/core/preferences.py:272
+msgid "Enable invitations"
+msgstr ""
+
+#: aleksis/core/preferences.py:280
+msgid "Length of invite code. (Default 3: abcde-acbde-abcde)"
+msgstr ""
+
+#: aleksis/core/preferences.py:288
+msgid "Size of packets. (Default 5: abcde)"
+msgstr ""
+
+#: aleksis/core/preferences.py:298
 #, fuzzy
 #| msgid "Contact details"
 msgid "Allowed Grant Flows for OAuth applications"
 msgstr "Détails de contact"
 
-#: aleksis/core/preferences.py:287
+#: aleksis/core/preferences.py:311
 msgid "Available languages"
 msgstr ""
 
-#: aleksis/core/preferences.py:299
+#: aleksis/core/preferences.py:323
 msgid "Send emails if data checks detect problems"
 msgstr ""
 
-#: aleksis/core/preferences.py:310
+#: aleksis/core/preferences.py:334
 msgid "Email recipients for data checks problem emails"
 msgstr ""
 
-#: aleksis/core/preferences.py:321
+#: aleksis/core/preferences.py:345
 msgid "Email recipient groups for data checks problem emails"
 msgstr ""
 
-#: aleksis/core/preferences.py:330
+#: aleksis/core/preferences.py:354
 msgid "Show dashboard to users without login"
 msgstr ""
 
-#: aleksis/core/preferences.py:339
+#: aleksis/core/preferences.py:363
 msgid "Allow users to edit their dashboard"
 msgstr ""
 
-#: aleksis/core/preferences.py:350
+#: aleksis/core/preferences.py:374
 msgid "Fields on person model which are editable by themselves."
 msgstr ""
 
-#: aleksis/core/preferences.py:364
+#: aleksis/core/preferences.py:388
 msgid "Editable fields on person model which should trigger a notification on change"
 msgstr ""
 
-#: aleksis/core/preferences.py:377
+#: aleksis/core/preferences.py:401
 msgid "Contact for notification if a person changes their data"
 msgstr ""
 
-#: aleksis/core/preferences.py:387
+#: aleksis/core/preferences.py:411
 msgid "PDF file expiration duration"
 msgstr ""
 
-#: aleksis/core/preferences.py:388
+#: aleksis/core/preferences.py:412
 msgid "in minutes"
 msgstr ""
 
-#: aleksis/core/preferences.py:398
+#: aleksis/core/preferences.py:422
 msgid "Automatically update the dashboard and its widgets"
 msgstr ""
 
-#: aleksis/core/preferences.py:408
+#: aleksis/core/preferences.py:432
 msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr ""
 
-#: aleksis/core/settings.py:474
+#: aleksis/core/settings.py:507
 msgid "English"
 msgstr ""
 
-#: aleksis/core/settings.py:475
+#: aleksis/core/settings.py:508
 msgid "German"
 msgstr ""
 
-#: aleksis/core/tables.py:19
+#: aleksis/core/tables.py:24
 #: aleksis/core/templates/core/announcement/list.html:36
 #: aleksis/core/templates/core/group/full.html:24
-#: aleksis/core/templates/core/person/full.html:23
+#: aleksis/core/templates/core/person/full.html:24
 #: aleksis/core/templates/oauth2_provider/application/detail.html:17
 msgid "Edit"
 msgstr ""
 
-#: aleksis/core/tables.py:21 aleksis/core/tables.py:89
-#: aleksis/core/tables.py:105
+#: aleksis/core/tables.py:26 aleksis/core/tables.py:94
+#: aleksis/core/tables.py:137
 #: aleksis/core/templates/core/announcement/list.html:22
 msgid "Actions"
 msgstr ""
 
-#: aleksis/core/tables.py:56 aleksis/core/tables.py:57
-#: aleksis/core/tables.py:71 aleksis/core/tables.py:87
-#: aleksis/core/tables.py:103
+#: aleksis/core/tables.py:61 aleksis/core/tables.py:62
+#: aleksis/core/tables.py:76 aleksis/core/tables.py:92
+#: aleksis/core/tables.py:135
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/group/full.html:31
 #: aleksis/core/templates/core/pages/delete.html:22
-#: aleksis/core/templates/core/person/full.html:30
+#: aleksis/core/templates/core/person/full.html:31
 #: aleksis/core/templates/oauth2_provider/application/detail.html:21
 msgid "Delete"
 msgstr ""
@@ -1264,11 +1289,11 @@ msgstr ""
 msgid "Account inactive"
 msgstr ""
 
-#: aleksis/core/templates/account/account_inactive.html:13
+#: aleksis/core/templates/account/account_inactive.html:14
 msgid "Account inactive."
 msgstr ""
 
-#: aleksis/core/templates/account/account_inactive.html:15
+#: aleksis/core/templates/account/account_inactive.html:17
 msgid ""
 "\n"
 "            This account is currently inactive. If you think this is an\n"
@@ -1420,11 +1445,11 @@ msgstr ""
 msgid "Signup closed"
 msgstr ""
 
-#: aleksis/core/templates/account/signup_closed.html:13
+#: aleksis/core/templates/account/signup_closed.html:14
 msgid "Signup closed."
 msgstr ""
 
-#: aleksis/core/templates/account/signup_closed.html:15
+#: aleksis/core/templates/account/signup_closed.html:17
 msgid ""
 "\n"
 "            This sign up is currently closed. If you think this is an\n"
@@ -1462,11 +1487,6 @@ msgid ""
 "          "
 msgstr ""
 
-#: aleksis/core/templates/account/verification_sent.html:30
-#, python-format
-msgid "<strong>Note:</strong> you can still <a href=\"%(email_url)s\">change your e-mail address</a>"
-msgstr ""
-
 #: aleksis/core/templates/core/additional_field/edit.html:6
 #: aleksis/core/templates/core/additional_field/edit.html:7
 msgid "Edit additional field"
@@ -1515,11 +1535,11 @@ msgid "Logged in as"
 msgstr ""
 
 #: aleksis/core/templates/core/base.html:175
-msgid "About AlekSIS — The Free School Information System"
+msgid "About AlekSIS® — The Free School Information System"
 msgstr ""
 
 #: aleksis/core/templates/core/base.html:183
-msgid "Impress"
+msgid "Imprint"
 msgstr ""
 
 #: aleksis/core/templates/core/base.html:191
@@ -1527,7 +1547,7 @@ msgid "Privacy Policy"
 msgstr ""
 
 #: aleksis/core/templates/core/base_print.html:72
-msgid "Powered by AlekSIS"
+msgid "Powered by AlekSIS®"
 msgstr ""
 
 #: aleksis/core/templates/core/dashboard_widget/create.html:8
@@ -1748,7 +1768,7 @@ msgid "Edit group"
 msgstr ""
 
 #: aleksis/core/templates/core/group/full.html:38
-#: aleksis/core/templates/core/person/full.html:37
+#: aleksis/core/templates/core/person/full.html:38
 msgid "Change preferences"
 msgstr ""
 
@@ -1841,36 +1861,46 @@ msgid "No notifications available yet."
 msgstr ""
 
 #: aleksis/core/templates/core/pages/about.html:6
-#: aleksis/core/templates/core/pages/about.html:15
-msgid "About AlekSIS"
+msgid "About AlekSIS®"
 msgstr ""
 
 #: aleksis/core/templates/core/pages/about.html:7
-msgid "AlekSIS – The Free School Information System"
+msgid "AlekSIS® – The Free School Information System"
+msgstr ""
+
+#: aleksis/core/templates/core/pages/about.html:15
+msgid "About AlekSIS"
 msgstr ""
 
 #: aleksis/core/templates/core/pages/about.html:17
 msgid ""
 "\n"
-"              This platform is powered by AlekSIS, a web-based school information system (SIS) which can be used\n"
+"              This platform is powered by AlekSIS®, a web-based school information system (SIS) which can be used\n"
 "              to manage and/or publish organisational artifacts of educational institutions. AlekSIS is free software and\n"
 "              can be used by anyone.\n"
 "            "
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:25
+#: aleksis/core/templates/core/pages/about.html:24
+msgid ""
+"\n"
+"              AlekSIS® is a registered trademark of the AlekSIS open source project, represented by Teckids e.V.\n"
+"            "
+msgstr ""
+
+#: aleksis/core/templates/core/pages/about.html:30
 msgid "Website of AlekSIS"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:26
+#: aleksis/core/templates/core/pages/about.html:31
 msgid "Source code"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:35
+#: aleksis/core/templates/core/pages/about.html:40
 msgid "Licence information"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:37
+#: aleksis/core/templates/core/pages/about.html:42
 msgid ""
 "\n"
 "              The core and the official apps of AlekSIS are licenced under the EUPL, version 1.2 or later. For licence\n"
@@ -1879,23 +1909,23 @@ msgid ""
 "            "
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:45
+#: aleksis/core/templates/core/pages/about.html:50
 msgid "Free/Open Source Licence"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:46
+#: aleksis/core/templates/core/pages/about.html:51
 msgid "Other Licence"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:50
+#: aleksis/core/templates/core/pages/about.html:55
 msgid "Full licence text"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:51
+#: aleksis/core/templates/core/pages/about.html:56
 msgid "More information about the EUPL"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:90
+#: aleksis/core/templates/core/pages/about.html:95
 #, python-format
 msgid ""
 "\n"
@@ -2142,19 +2172,21 @@ msgstr "Détails de contact"
 msgid "Edit person"
 msgstr ""
 
-#: aleksis/core/templates/core/person/full.html:44
-#: aleksis/core/templates/impersonate/list_users.html:7
-#: aleksis/core/templates/impersonate/list_users.html:8
+#: aleksis/core/templates/core/person/full.html:45
 #, fuzzy
 #| msgid "Contact details"
 msgid "Impersonate"
 msgstr "Détails de contact"
 
-#: aleksis/core/templates/core/person/full.html:50
+#: aleksis/core/templates/core/person/full.html:51
+msgid "Invite user"
+msgstr ""
+
+#: aleksis/core/templates/core/person/full.html:57
 msgid "Contact details"
 msgstr "Détails de contact"
 
-#: aleksis/core/templates/core/person/full.html:131
+#: aleksis/core/templates/core/person/full.html:138
 msgid "Children"
 msgstr ""
 
@@ -2194,6 +2226,51 @@ msgstr ""
 msgid "Save preferences"
 msgstr ""
 
+#: aleksis/core/templates/invitations/enter.html:21
+msgid "Accept your invitation"
+msgstr ""
+
+#: aleksis/core/templates/invitations/enter.html:25
+msgid ""
+"\n"
+"                Please enter your invitation code to register\n"
+"                your new user account:\n"
+"              "
+msgstr ""
+
+#: aleksis/core/templates/invitations/enter.html:37
+msgid "Accept invite"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:9
+#: aleksis/core/templates/invitations/forms/_invite.html:10
+#: aleksis/core/templates/invitations/forms/_invite.html:21
+msgid "Invite"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:17
+#, fuzzy
+#| msgid "Contact details"
+msgid "Invite by email address"
+msgstr "Détails de contact"
+
+#: aleksis/core/templates/invitations/forms/_invite.html:26
+msgid "Generate invitation code"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:29
+msgid "Generate code"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:33
+msgid "Invitations"
+msgstr ""
+
+#: aleksis/core/templates/invitations/messages/invite_accepted.txt:3
+#, python-format
+msgid "The invitation for %(email)s has been accepted."
+msgstr ""
+
 #: aleksis/core/templates/oauth2_provider/application/create.html:5
 #: aleksis/core/templates/oauth2_provider/application/create.html:6
 #, fuzzy
@@ -2860,151 +2937,166 @@ msgstr ""
 msgid "SMS"
 msgstr ""
 
-#: aleksis/core/util/pdf.py:105
+#: aleksis/core/util/pdf.py:113
 msgid "Progress: Generate PDF file"
 msgstr ""
 
-#: aleksis/core/util/pdf.py:106
+#: aleksis/core/util/pdf.py:114
 msgid "Generating PDF file …"
 msgstr ""
 
-#: aleksis/core/util/pdf.py:107
+#: aleksis/core/util/pdf.py:115
 msgid "The PDF file has been generated successfully."
 msgstr ""
 
-#: aleksis/core/util/pdf.py:108
+#: aleksis/core/util/pdf.py:116
 msgid "There was a problem while generating the PDF file."
 msgstr ""
 
-#: aleksis/core/util/pdf.py:111
+#: aleksis/core/util/pdf.py:119
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/core/views.py:270
+#: aleksis/core/views.py:280
 msgid "The school term has been created."
 msgstr ""
 
-#: aleksis/core/views.py:282
+#: aleksis/core/views.py:292
 msgid "The school term has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:406
+#: aleksis/core/views.py:416
 msgid "The child groups were successfully saved."
 msgstr ""
 
-#: aleksis/core/views.py:425 aleksis/core/views.py:435
+#: aleksis/core/views.py:435 aleksis/core/views.py:445
 msgid "The person has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:485
+#: aleksis/core/views.py:495
 msgid "The group has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:582
+#: aleksis/core/views.py:592
 msgid "The announcement has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:598
+#: aleksis/core/views.py:608
 msgid "The announcement has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:682
+#: aleksis/core/views.py:695
 msgid "The preferences have been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:706
+#: aleksis/core/views.py:719
 msgid "The person has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:720
+#: aleksis/core/views.py:733
 msgid "The group has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:752
+#: aleksis/core/views.py:765
 msgid "The additional_field has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:786
+#: aleksis/core/views.py:799
 msgid "The additional field has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:811
+#: aleksis/core/views.py:824
 msgid "The group type has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:841
+#: aleksis/core/views.py:854
 msgid "The group type has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:874
+#: aleksis/core/views.py:887
 msgid "Progress: Run data checks"
 msgstr ""
 
-#: aleksis/core/views.py:875
+#: aleksis/core/views.py:888
 msgid "Run data checks …"
 msgstr ""
 
-#: aleksis/core/views.py:876
+#: aleksis/core/views.py:889
 msgid "The data checks were run successfully."
 msgstr ""
 
-#: aleksis/core/views.py:877
+#: aleksis/core/views.py:890
 msgid "There was a problem while running data checks."
 msgstr ""
 
-#: aleksis/core/views.py:893
+#: aleksis/core/views.py:906
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr ""
 
-#: aleksis/core/views.py:935
+#: aleksis/core/views.py:948
 msgid "The dashboard widget has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:965
+#: aleksis/core/views.py:978
 msgid "The dashboard widget has been created."
 msgstr ""
 
-#: aleksis/core/views.py:975
+#: aleksis/core/views.py:988
 msgid "The dashboard widget has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1042
+#: aleksis/core/views.py:1055
 msgid "Your dashboard configuration has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1044
+#: aleksis/core/views.py:1057
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1139
+#: aleksis/core/views.py:1127
+#, python-brace-format
+msgid "The invitation was successfully created. The invitation code is {code}"
+msgstr ""
+
+#: aleksis/core/views.py:1218
 msgid "We have successfully assigned the permissions."
 msgstr ""
 
-#: aleksis/core/views.py:1149
+#: aleksis/core/views.py:1228
 msgid "The global user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1159
+#: aleksis/core/views.py:1238
 msgid "The global group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1169
+#: aleksis/core/views.py:1248
 msgid "The object user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1179
+#: aleksis/core/views.py:1258
 msgid "The object group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1298
+#: aleksis/core/views.py:1377
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr ""
 
-#: aleksis/core/views.py:1305
+#: aleksis/core/views.py:1384
 msgid "The third-party account has been successfully disconnected."
 msgstr ""
 
+#: aleksis/core/views.py:1441
+msgid "Person was invited successfully."
+msgstr ""
+
+#: aleksis/core/views.py:1443
+#, fuzzy
+#| msgid "This username is already in use."
+msgid "Person was already invited."
+msgstr "Cet nom est deja en utilisation."
+
 #, fuzzy
 #~| msgid "Contact details"
 #~ msgid "Can link persons to accounts"
diff --git a/aleksis/core/locale/fr/LC_MESSAGES/djangojs.po b/aleksis/core/locale/fr/LC_MESSAGES/djangojs.po
index d92f36a6a9ed2888791de39ed7c9db3bdfe7f8dc..8dd920515ef4b23a59c72cb6ec2809d27d03292a 100644
--- a/aleksis/core/locale/fr/LC_MESSAGES/djangojs.po
+++ b/aleksis/core/locale/fr/LC_MESSAGES/djangojs.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-11-29 09:59+0100\n"
+"POT-Creation-Date: 2021-12-28 12:14+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -30,6 +30,6 @@ msgstr ""
 msgid "OK"
 msgstr ""
 
-#: aleksis/core/static/js/main.js:127
+#: aleksis/core/static/js/main.js:128
 msgid "This page may contain outdated information since there is no internet connection."
 msgstr ""
diff --git a/aleksis/core/locale/la/LC_MESSAGES/django.po b/aleksis/core/locale/la/LC_MESSAGES/django.po
index 0f4d9b00d9560e0140e6242ec635d51b278c1271..b8f7f6b758f1520e97d0d96304f9205d2e356c9a 100644
--- a/aleksis/core/locale/la/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/la/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-11-29 09:59+0100\n"
+"POT-Creation-Date: 2021-12-28 12:13+0100\n"
 "PO-Revision-Date: 2020-12-19 12:57+0000\n"
 "Last-Translator: Julian <leuckerj@gmail.com>\n"
 "Language-Team: Latin <https://translate.edugit.org/projects/aleksis/aleksis/la/>\n"
@@ -18,32 +18,39 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
 "X-Generator: Weblate 4.3.2\n"
 
-#: aleksis/core/apps.py:151
+#: aleksis/core/apps.py:152
 msgid "OpenID Connect scope"
 msgstr ""
 
-#: aleksis/core/apps.py:152
+#: aleksis/core/apps.py:153
 msgid "Given name, family name, link to profile and picture if existing."
 msgstr ""
 
-#: aleksis/core/apps.py:153
+#: aleksis/core/apps.py:154
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Full home postal address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/apps.py:154
+#: aleksis/core/apps.py:155
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Email address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/apps.py:155
+#: aleksis/core/apps.py:156
 #, fuzzy
 #| msgid "Mobile phone"
 msgid "Home and mobile phone"
 msgstr "Numerus telephoni mobilis"
 
+#: aleksis/core/apps.py:157 aleksis/core/forms.py:223 aleksis/core/menus.py:265
+#: aleksis/core/models.py:414 aleksis/core/templates/core/group/list.html:8
+#: aleksis/core/templates/core/group/list.html:9
+#: aleksis/core/templates/core/person/full.html:152
+msgid "Groups"
+msgstr "Greges"
+
 #: aleksis/core/data_checks.py:55
 msgid "Ignore problem"
 msgstr ""
@@ -94,168 +101,162 @@ msgstr ""
 msgid "Content type"
 msgstr ""
 
-#: aleksis/core/filters.py:112 aleksis/core/models.py:579
+#: aleksis/core/filters.py:112 aleksis/core/models.py:581
 msgid "User"
 msgstr ""
 
-#: aleksis/core/filters.py:134 aleksis/core/models.py:411
+#: aleksis/core/filters.py:134 aleksis/core/models.py:413
 msgid "Group"
 msgstr "Grex"
 
-#: aleksis/core/forms.py:46 aleksis/core/forms.py:514
+#: aleksis/core/forms.py:48 aleksis/core/forms.py:559
 msgid "Base data"
 msgstr ""
 
-#: aleksis/core/forms.py:52
+#: aleksis/core/forms.py:54
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/forms.py:53
+#: aleksis/core/forms.py:55 aleksis/core/forms.py:568
 msgid "Contact data"
 msgstr ""
 
-#: aleksis/core/forms.py:55
+#: aleksis/core/forms.py:57
 msgid "Advanced personal data"
 msgstr ""
 
-#: aleksis/core/forms.py:103
+#: aleksis/core/forms.py:105
 msgid "New user"
 msgstr ""
 
-#: aleksis/core/forms.py:103
+#: aleksis/core/forms.py:105
 #, fuzzy
 #| msgid "Persons and accounts"
 msgid "Create a new account"
 msgstr "Personae et computi"
 
-#: aleksis/core/forms.py:134
+#: aleksis/core/forms.py:136
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr ""
 
-#: aleksis/core/forms.py:138
+#: aleksis/core/forms.py:140
 msgid "This username is already in use."
 msgstr ""
 
-#: aleksis/core/forms.py:155 aleksis/core/models.py:125
+#: aleksis/core/forms.py:157 aleksis/core/models.py:129
 msgid "School term"
 msgstr "Anus scolae"
 
-#: aleksis/core/forms.py:156
+#: aleksis/core/forms.py:158
 #, fuzzy
 #| msgid "Data management"
 msgid "Common data"
 msgstr "Adminstratio datarum"
 
-#: aleksis/core/forms.py:157 aleksis/core/forms.py:208
-#: aleksis/core/menus.py:256 aleksis/core/models.py:148
+#: aleksis/core/forms.py:159 aleksis/core/forms.py:210
+#: aleksis/core/menus.py:254 aleksis/core/models.py:152
 #: aleksis/core/templates/core/person/list.html:8
 #: aleksis/core/templates/core/person/list.html:9
 msgid "Persons"
 msgstr "personae"
 
-#: aleksis/core/forms.py:158
+#: aleksis/core/forms.py:160 aleksis/core/forms.py:570
 #, fuzzy
 #| msgid "Additional name(s)"
 msgid "Additional data"
 msgstr "addita nomines"
 
-#: aleksis/core/forms.py:200 aleksis/core/forms.py:203
-#: aleksis/core/models.py:68
+#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
+#: aleksis/core/models.py:72
 msgid "Date"
 msgstr "dies"
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:76
+#: aleksis/core/forms.py:203 aleksis/core/forms.py:206
+#: aleksis/core/models.py:80
 msgid "Time"
 msgstr "tempus"
 
-#: aleksis/core/forms.py:221 aleksis/core/menus.py:267
-#: aleksis/core/models.py:412 aleksis/core/templates/core/group/list.html:8
-#: aleksis/core/templates/core/group/list.html:9
-#: aleksis/core/templates/core/person/full.html:145
-msgid "Groups"
-msgstr "Greges"
-
-#: aleksis/core/forms.py:234
+#: aleksis/core/forms.py:236
 msgid "From when until when should the announcement be displayed?"
 msgstr ""
 
-#: aleksis/core/forms.py:237
+#: aleksis/core/forms.py:239
 msgid "Who should see the announcement?"
 msgstr "Quis nuntium videatne?"
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:240
 msgid "Write your announcement:"
 msgstr "Scribe nuntium:"
 
-#: aleksis/core/forms.py:277
+#: aleksis/core/forms.py:279
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr ""
 
-#: aleksis/core/forms.py:281
+#: aleksis/core/forms.py:283
 msgid "The from date and time must be earlier then the until date and time."
 msgstr ""
 
-#: aleksis/core/forms.py:290
+#: aleksis/core/forms.py:292
 msgid "You need at least one recipient."
 msgstr ""
 
-#: aleksis/core/forms.py:411
+#: aleksis/core/forms.py:401
+msgid "Invitation code"
+msgstr ""
+
+#: aleksis/core/forms.py:402
+msgid "Please enter your invitation code."
+msgstr ""
+
+#: aleksis/core/forms.py:434
 #, fuzzy
 #| msgid "Who should see the announcement?"
 msgid "Who should get the permission?"
 msgstr "Quis nuntium videatne?"
 
-#: aleksis/core/forms.py:412
+#: aleksis/core/forms.py:435
 msgid "On what?"
 msgstr ""
 
-#: aleksis/core/forms.py:438
+#: aleksis/core/forms.py:461
 msgid "Select objects which the permission should be granted for:"
 msgstr ""
 
-#: aleksis/core/forms.py:441
+#: aleksis/core/forms.py:464
 msgid "Grant the permission for all objects"
 msgstr ""
 
-#: aleksis/core/forms.py:449
+#: aleksis/core/forms.py:472
 msgid "You must select at least one group or person which should get the permission."
 msgstr ""
 
-#: aleksis/core/forms.py:454
+#: aleksis/core/forms.py:477
 msgid "You must grant the permission to all objects and/or to some objects."
 msgstr ""
 
-#: aleksis/core/forms.py:518
+#: aleksis/core/forms.py:564
+#, fuzzy
+#| msgid "E-mail address"
+msgid "Adress data"
+msgstr "Inscriptio electronica"
+
+#: aleksis/core/forms.py:576
 #, fuzzy
 #| msgid "Data management"
 msgid "Account data"
 msgstr "Adminstratio datarum"
 
-#: aleksis/core/forms.py:524
-msgid "Consents"
-msgstr ""
-
-#: aleksis/core/forms.py:531
+#: aleksis/core/forms.py:583
 msgid "Password"
 msgstr ""
 
-#: aleksis/core/forms.py:537
+#: aleksis/core/forms.py:586
 msgid "Password (again)"
 msgstr ""
 
-#: aleksis/core/forms.py:550
-#, python-brace-format
-msgid "I have read the <a href='{privacy_policy}'>Privacy policy</a> and agree with them."
-msgstr ""
-
-#: aleksis/core/forms.py:575
-msgid "You must type the same password each time."
-msgstr ""
-
-#: aleksis/core/forms.py:720
+#: aleksis/core/forms.py:752
 msgid "No valid selection."
 msgstr ""
 
@@ -265,24 +266,20 @@ msgstr ""
 msgid "There are unresolved data problems."
 msgstr "Scribe nuntium:"
 
-#: aleksis/core/health_checks.py:38
-msgid "The backup folder doesn't exist."
-msgstr ""
-
-#: aleksis/core/health_checks.py:47
+#: aleksis/core/health_checks.py:44
 #, python-brace-format
 msgid "Last backup {time_gone_since_backup}!"
 msgstr ""
 
-#: aleksis/core/health_checks.py:49
+#: aleksis/core/health_checks.py:46
 msgid "No backup found!"
 msgstr ""
 
-#: aleksis/core/health_checks.py:76
+#: aleksis/core/health_checks.py:73
 msgid "No backup result found!"
 msgstr ""
 
-#: aleksis/core/health_checks.py:78
+#: aleksis/core/health_checks.py:75
 #, python-brace-format
 msgid "{task.status} - {task.result}"
 msgstr ""
@@ -298,34 +295,40 @@ msgstr "nomen profiteri"
 msgid "Sign up"
 msgstr ""
 
-#: aleksis/core/menus.py:24
+#: aleksis/core/menus.py:24 aleksis/core/templates/invitations/enter.html:7
+#, fuzzy
+#| msgid "Edit school information"
+msgid "Accept invitation"
+msgstr "Muta informationes scolae"
+
+#: aleksis/core/menus.py:33
 msgid "Dashboard"
 msgstr "Forum"
 
-#: aleksis/core/menus.py:32 aleksis/core/models.py:625
-#: aleksis/core/preferences.py:27
+#: aleksis/core/menus.py:41 aleksis/core/models.py:627
+#: aleksis/core/preferences.py:28
 #: aleksis/core/templates/core/notifications.html:4
 #: aleksis/core/templates/core/notifications.html:5
 msgid "Notifications"
 msgstr "Nuntii"
 
-#: aleksis/core/menus.py:44
+#: aleksis/core/menus.py:53
 msgid "Account"
 msgstr ""
 
-#: aleksis/core/menus.py:51
+#: aleksis/core/menus.py:60
 msgid "Stop impersonation"
 msgstr "Simulandum aliquem finire"
 
-#: aleksis/core/menus.py:60 aleksis/core/templates/core/base.html:80
+#: aleksis/core/menus.py:69 aleksis/core/templates/core/base.html:80
 msgid "Logout"
 msgstr "nomen retractare"
 
-#: aleksis/core/menus.py:66
+#: aleksis/core/menus.py:75
 msgid "2FA"
 msgstr ""
 
-#: aleksis/core/menus.py:74
+#: aleksis/core/menus.py:83
 #: aleksis/core/templates/account/password_change.html:5
 #: aleksis/core/templates/account/password_change.html:6
 #: aleksis/core/templates/account/password_change.html:19
@@ -337,22 +340,22 @@ msgstr ""
 msgid "Change password"
 msgstr ""
 
-#: aleksis/core/menus.py:86
+#: aleksis/core/menus.py:95
 msgid "Me"
 msgstr ""
 
-#: aleksis/core/menus.py:95
+#: aleksis/core/menus.py:104
 #: aleksis/core/templates/dynamic_preferences/form.html:5
 msgid "Preferences"
 msgstr ""
 
-#: aleksis/core/menus.py:104
+#: aleksis/core/menus.py:113
 #, fuzzy
 #| msgid "Persons and accounts"
 msgid "Third-party accounts"
 msgstr "Personae et computi"
 
-#: aleksis/core/menus.py:113
+#: aleksis/core/menus.py:122
 #: aleksis/core/templates/oauth2_provider/authorized-tokens.html:5
 #: aleksis/core/templates/oauth2_provider/authorized-tokens.html:6
 #, fuzzy
@@ -360,23 +363,23 @@ msgstr "Personae et computi"
 msgid "Authorized applications"
 msgstr "Nuntii"
 
-#: aleksis/core/menus.py:124
+#: aleksis/core/menus.py:133
 msgid "Admin"
 msgstr "Administratio"
 
-#: aleksis/core/menus.py:132 aleksis/core/models.py:725
+#: aleksis/core/menus.py:141 aleksis/core/models.py:727
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr "Nuntii"
 
-#: aleksis/core/menus.py:143 aleksis/core/models.py:126
+#: aleksis/core/menus.py:152 aleksis/core/models.py:130
 #: aleksis/core/templates/core/school_term/list.html:8
 #: aleksis/core/templates/core/school_term/list.html:9
 msgid "School terms"
 msgstr "ani scolae"
 
-#: aleksis/core/menus.py:154
+#: aleksis/core/menus.py:163
 #: aleksis/core/templates/core/dashboard_widget/list.html:8
 #: aleksis/core/templates/core/dashboard_widget/list.html:9
 #, fuzzy
@@ -384,45 +387,41 @@ msgstr "ani scolae"
 msgid "Dashboard widgets"
 msgstr "Forum"
 
-#: aleksis/core/menus.py:165
+#: aleksis/core/menus.py:174
 #: aleksis/core/templates/core/management/data_management.html:6
 #: aleksis/core/templates/core/management/data_management.html:7
 msgid "Data management"
 msgstr "Adminstratio datarum"
 
-#: aleksis/core/menus.py:176
+#: aleksis/core/menus.py:185
 #: aleksis/core/templates/core/pages/system_status.html:5
 #: aleksis/core/templates/core/pages/system_status.html:7
 msgid "System status"
 msgstr "Status systemae"
 
-#: aleksis/core/menus.py:187
-msgid "Impersonation"
-msgstr "Simulare aliquem"
-
-#: aleksis/core/menus.py:198
+#: aleksis/core/menus.py:196
 #, fuzzy
 #| msgid "Notification"
 msgid "Configuration"
 msgstr "Nuntius"
 
-#: aleksis/core/menus.py:209 aleksis/core/templates/core/data_check/list.html:9
+#: aleksis/core/menus.py:207 aleksis/core/templates/core/data_check/list.html:9
 #: aleksis/core/templates/core/data_check/list.html:10
 #, fuzzy
 #| msgid "System status"
 msgid "Data checks"
 msgstr "Status systemae"
 
-#: aleksis/core/menus.py:215 aleksis/core/templates/core/perms/list.html:13
+#: aleksis/core/menus.py:213 aleksis/core/templates/core/perms/list.html:13
 #: aleksis/core/templates/core/perms/list.html:14
 msgid "Manage permissions"
 msgstr ""
 
-#: aleksis/core/menus.py:226
+#: aleksis/core/menus.py:224
 msgid "Backend Admin"
 msgstr ""
 
-#: aleksis/core/menus.py:234
+#: aleksis/core/menus.py:232
 #: aleksis/core/templates/oauth2_provider/application/list.html:5
 #: aleksis/core/templates/oauth2_provider/application/list.html:6
 #, fuzzy
@@ -430,11 +429,11 @@ msgstr ""
 msgid "OAuth2 Applications"
 msgstr "Nuntii"
 
-#: aleksis/core/menus.py:247
+#: aleksis/core/menus.py:245
 msgid "People"
 msgstr "Personae"
 
-#: aleksis/core/menus.py:278 aleksis/core/models.py:979
+#: aleksis/core/menus.py:276 aleksis/core/models.py:981
 #: aleksis/core/templates/core/group_type/list.html:8
 #: aleksis/core/templates/core/group_type/list.html:9
 #, fuzzy
@@ -442,11 +441,11 @@ msgstr "Personae"
 msgid "Group types"
 msgstr "Greges"
 
-#: aleksis/core/menus.py:289
+#: aleksis/core/menus.py:287
 msgid "Groups and child groups"
 msgstr ""
 
-#: aleksis/core/menus.py:300 aleksis/core/models.py:460
+#: aleksis/core/menus.py:298 aleksis/core/models.py:462
 #: aleksis/core/templates/core/additional_field/list.html:8
 #: aleksis/core/templates/core/additional_field/list.html:9
 #, fuzzy
@@ -454,809 +453,839 @@ msgstr ""
 msgid "Additional fields"
 msgstr "addita nomines"
 
-#: aleksis/core/menus.py:315
+#: aleksis/core/menus.py:309
+#, fuzzy
+#| msgid "Stop impersonation"
+msgid "Invite person"
+msgstr "Simulandum aliquem finire"
+
+#: aleksis/core/menus.py:322
 #: aleksis/core/templates/core/group/child_groups.html:7
 #: aleksis/core/templates/core/group/child_groups.html:9
 msgid "Assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/mixins.py:508
+#: aleksis/core/mixins.py:511
 #, fuzzy
 #| msgid "Edit school term"
 msgid "Linked school term"
 msgstr "Muta anum scolae"
 
-#: aleksis/core/models.py:66
+#: aleksis/core/models.py:70
 msgid "Boolean (Yes/No)"
 msgstr ""
 
-#: aleksis/core/models.py:67
+#: aleksis/core/models.py:71
 msgid "Text (one line)"
 msgstr ""
 
-#: aleksis/core/models.py:69
+#: aleksis/core/models.py:73
 msgid "Date and time"
 msgstr "Dies et hora"
 
-#: aleksis/core/models.py:70
+#: aleksis/core/models.py:74
 msgid "Decimal number"
 msgstr ""
 
-#: aleksis/core/models.py:71 aleksis/core/models.py:194
+#: aleksis/core/models.py:75 aleksis/core/models.py:198
 msgid "E-mail address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:72
+#: aleksis/core/models.py:76
 msgid "Integer"
 msgstr ""
 
-#: aleksis/core/models.py:73
+#: aleksis/core/models.py:77
 #, fuzzy
 #| msgid "E-mail address"
 msgid "IP address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:74
+#: aleksis/core/models.py:78
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr ""
 
-#: aleksis/core/models.py:75
+#: aleksis/core/models.py:79
 msgid "Text (multi-line)"
 msgstr ""
 
-#: aleksis/core/models.py:77
+#: aleksis/core/models.py:81
 msgid "URL / Link"
 msgstr ""
 
-#: aleksis/core/models.py:89 aleksis/core/models.py:948
+#: aleksis/core/models.py:93 aleksis/core/models.py:950
 msgid "Name"
 msgstr "Nomen"
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:95
 msgid "Start date"
 msgstr ""
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:96
 msgid "End date"
 msgstr ""
 
-#: aleksis/core/models.py:111
+#: aleksis/core/models.py:115
 msgid "The start date must be earlier than the end date."
 msgstr ""
 
-#: aleksis/core/models.py:118
+#: aleksis/core/models.py:122
 msgid "There is already a school term for this time or a part of this time."
 msgstr ""
 
-#: aleksis/core/models.py:147 aleksis/core/models.py:897
+#: aleksis/core/models.py:151 aleksis/core/models.py:899
 msgid "Person"
 msgstr "Persona"
 
-#: aleksis/core/models.py:150
+#: aleksis/core/models.py:154
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Can view address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:151
+#: aleksis/core/models.py:155
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Can view contact details"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:152
+#: aleksis/core/models.py:156
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Can view photo"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/models.py:153
+#: aleksis/core/models.py:157
 #, fuzzy
 #| msgid "Persons and accounts"
 msgid "Can view persons groups"
 msgstr "Personae et computi"
 
-#: aleksis/core/models.py:154
+#: aleksis/core/models.py:158
 #, fuzzy
 #| msgid "Stop impersonation"
 msgid "Can view personal details"
 msgstr "Simulandum aliquem finire"
 
-#: aleksis/core/models.py:164
+#: aleksis/core/models.py:168
 msgid "female"
 msgstr "femininum"
 
-#: aleksis/core/models.py:164
+#: aleksis/core/models.py:168
 msgid "male"
 msgstr "maskulinum"
 
-#: aleksis/core/models.py:172
+#: aleksis/core/models.py:176 aleksis/core/models.py:1144
 msgid "Linked user"
 msgstr ""
 
-#: aleksis/core/models.py:174
+#: aleksis/core/models.py:178
 #, fuzzy
 #| msgid "Impersonation"
 msgid "Is person active?"
 msgstr "Simulare aliquem"
 
-#: aleksis/core/models.py:176
+#: aleksis/core/models.py:180
 msgid "First name"
 msgstr "Primus nomen"
 
-#: aleksis/core/models.py:177
+#: aleksis/core/models.py:181
 msgid "Last name"
 msgstr "Secondus nomen"
 
-#: aleksis/core/models.py:179
+#: aleksis/core/models.py:183
 msgid "Additional name(s)"
 msgstr "addita nomines"
 
-#: aleksis/core/models.py:183 aleksis/core/models.py:429
+#: aleksis/core/models.py:187 aleksis/core/models.py:431
 msgid "Short name"
 msgstr "Breve nomen"
 
-#: aleksis/core/models.py:186
+#: aleksis/core/models.py:190
 msgid "Street"
 msgstr "Via"
 
-#: aleksis/core/models.py:187
+#: aleksis/core/models.py:191
 msgid "Street number"
 msgstr "Numerus domini"
 
-#: aleksis/core/models.py:188
+#: aleksis/core/models.py:192
 msgid "Postal code"
 msgstr "Numerus directorius"
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:193
 msgid "Place"
 msgstr "Urbs"
 
-#: aleksis/core/models.py:191
+#: aleksis/core/models.py:195
 msgid "Home phone"
 msgstr "Numerus telephoni domi"
 
-#: aleksis/core/models.py:192
+#: aleksis/core/models.py:196
 msgid "Mobile phone"
 msgstr "Numerus telephoni mobilis"
 
-#: aleksis/core/models.py:196
+#: aleksis/core/models.py:200
 msgid "Date of birth"
 msgstr "Dies natalis"
 
-#: aleksis/core/models.py:198
+#: aleksis/core/models.py:201
 #, fuzzy
 #| msgid "Date of birth"
 msgid "Place of birth"
 msgstr "Dies natalis"
 
-#: aleksis/core/models.py:200
+#: aleksis/core/models.py:202
 msgid "Sex"
 msgstr "Genus"
 
-#: aleksis/core/models.py:202
+#: aleksis/core/models.py:204
 msgid "Photo"
 msgstr "Photographia"
 
-#: aleksis/core/models.py:206 aleksis/core/templates/core/person/full.html:138
+#: aleksis/core/models.py:208 aleksis/core/templates/core/person/full.html:145
 msgid "Guardians / Parents"
 msgstr "Parentes"
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:215
 msgid "Primary group"
 msgstr ""
 
-#: aleksis/core/models.py:216 aleksis/core/models.py:583
-#: aleksis/core/models.py:607 aleksis/core/models.py:692
-#: aleksis/core/models.py:972 aleksis/core/templates/core/person/full.html:121
+#: aleksis/core/models.py:218 aleksis/core/models.py:585
+#: aleksis/core/models.py:609 aleksis/core/models.py:694
+#: aleksis/core/models.py:974 aleksis/core/templates/core/person/full.html:128
 msgid "Description"
 msgstr "Descriptio"
 
-#: aleksis/core/models.py:384
+#: aleksis/core/models.py:386
 msgid "Title of field"
 msgstr ""
 
-#: aleksis/core/models.py:386
+#: aleksis/core/models.py:388
 msgid "Type of field"
 msgstr ""
 
-#: aleksis/core/models.py:393
+#: aleksis/core/models.py:395
 #, fuzzy
 #| msgid "Additional name(s)"
 msgid "Addtitional field for groups"
 msgstr "addita nomines"
 
-#: aleksis/core/models.py:394
+#: aleksis/core/models.py:396
 #, fuzzy
 #| msgid "Additional name(s)"
 msgid "Addtitional fields for groups"
 msgstr "addita nomines"
 
-#: aleksis/core/models.py:414
+#: aleksis/core/models.py:416
 msgid "Can assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/models.py:415
+#: aleksis/core/models.py:417
 #, fuzzy
 #| msgid "Persons and accounts"
 msgid "Can view statistics about group."
 msgstr "Personae et computi"
 
-#: aleksis/core/models.py:427
+#: aleksis/core/models.py:429
 #, fuzzy
 #| msgid "Last name"
 msgid "Long name"
 msgstr "Secondus nomen"
 
-#: aleksis/core/models.py:437 aleksis/core/templates/core/group/full.html:85
+#: aleksis/core/models.py:439 aleksis/core/templates/core/group/full.html:85
 msgid "Members"
 msgstr ""
 
-#: aleksis/core/models.py:440 aleksis/core/templates/core/group/full.html:82
+#: aleksis/core/models.py:442 aleksis/core/templates/core/group/full.html:82
 msgid "Owners"
 msgstr ""
 
-#: aleksis/core/models.py:447 aleksis/core/templates/core/group/full.html:55
+#: aleksis/core/models.py:449 aleksis/core/templates/core/group/full.html:55
 msgid "Parent groups"
 msgstr ""
 
-#: aleksis/core/models.py:455
+#: aleksis/core/models.py:457
 msgid "Type of group"
 msgstr ""
 
-#: aleksis/core/models.py:582 aleksis/core/models.py:606
-#: aleksis/core/models.py:691
+#: aleksis/core/models.py:584 aleksis/core/models.py:608
+#: aleksis/core/models.py:693
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr "Titulus"
 
-#: aleksis/core/models.py:585
+#: aleksis/core/models.py:587
 msgid "Application"
 msgstr ""
 
-#: aleksis/core/models.py:591
+#: aleksis/core/models.py:593
 msgid "Activity"
 msgstr ""
 
-#: aleksis/core/models.py:592
+#: aleksis/core/models.py:594
 msgid "Activities"
 msgstr ""
 
-#: aleksis/core/models.py:598
+#: aleksis/core/models.py:600
 msgid "Sender"
 msgstr "Mittens"
 
-#: aleksis/core/models.py:603
+#: aleksis/core/models.py:605
 msgid "Recipient"
 msgstr ""
 
-#: aleksis/core/models.py:608 aleksis/core/models.py:949
+#: aleksis/core/models.py:610 aleksis/core/models.py:951
 msgid "Link"
 msgstr ""
 
-#: aleksis/core/models.py:610
+#: aleksis/core/models.py:612
 msgid "Read"
 msgstr ""
 
-#: aleksis/core/models.py:611
+#: aleksis/core/models.py:613
 msgid "Sent"
 msgstr ""
 
-#: aleksis/core/models.py:624
+#: aleksis/core/models.py:626
 #, fuzzy
 #| msgid "Notifications"
 msgid "Notification"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:693
+#: aleksis/core/models.py:695
 msgid "Link to detailed view"
 msgstr ""
 
-#: aleksis/core/models.py:696
+#: aleksis/core/models.py:698
 msgid "Date and time from when to show"
 msgstr ""
 
-#: aleksis/core/models.py:699
+#: aleksis/core/models.py:701
 msgid "Date and time until when to show"
 msgstr ""
 
-#: aleksis/core/models.py:724
+#: aleksis/core/models.py:726
 #, fuzzy
 #| msgid "Announcements"
 msgid "Announcement"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:762
+#: aleksis/core/models.py:764
 #, fuzzy
 #| msgid "Announcements"
 msgid "Announcement recipient"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:763
+#: aleksis/core/models.py:765
 #, fuzzy
 #| msgid "Announcements"
 msgid "Announcement recipients"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:818
+#: aleksis/core/models.py:820
 #, fuzzy
 #| msgid "Site title"
 msgid "Widget Title"
 msgstr "Titulus paginae"
 
-#: aleksis/core/models.py:819
+#: aleksis/core/models.py:821
 msgid "Activate Widget"
 msgstr ""
 
-#: aleksis/core/models.py:820
+#: aleksis/core/models.py:822
 #, fuzzy
 #| msgid "Site title"
 msgid "Widget is broken"
 msgstr "Titulus paginae"
 
-#: aleksis/core/models.py:823
+#: aleksis/core/models.py:825
 msgid "Size on mobile devices"
 msgstr ""
 
-#: aleksis/core/models.py:824
+#: aleksis/core/models.py:826
 msgid "<= 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:829
+#: aleksis/core/models.py:831
 msgid "Size on tablet devices"
 msgstr ""
 
-#: aleksis/core/models.py:830
+#: aleksis/core/models.py:832
 msgid "> 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:835
+#: aleksis/core/models.py:837
 msgid "Size on desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:836
+#: aleksis/core/models.py:838
 msgid "> 992 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:841
+#: aleksis/core/models.py:843
 msgid "Size on large desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:842
+#: aleksis/core/models.py:844
 msgid "> 1200 px>, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:873
+#: aleksis/core/models.py:875
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Can edit default dashboard"
 msgstr "Forum"
 
-#: aleksis/core/models.py:874
+#: aleksis/core/models.py:876
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Dashboard Widget"
 msgstr "Forum"
 
-#: aleksis/core/models.py:875
+#: aleksis/core/models.py:877
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Dashboard Widgets"
 msgstr "Forum"
 
-#: aleksis/core/models.py:881
+#: aleksis/core/models.py:883
 msgid "URL"
 msgstr ""
 
-#: aleksis/core/models.py:882
+#: aleksis/core/models.py:884
 #, fuzzy
 #| msgid "Icon"
 msgid "Icon URL"
 msgstr "Nota"
 
-#: aleksis/core/models.py:888
+#: aleksis/core/models.py:890
 msgid "External link widget"
 msgstr ""
 
-#: aleksis/core/models.py:889
+#: aleksis/core/models.py:891
 msgid "External link widgets"
 msgstr ""
 
-#: aleksis/core/models.py:894
+#: aleksis/core/models.py:896
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Dashboard widget"
 msgstr "Forum"
 
-#: aleksis/core/models.py:899
+#: aleksis/core/models.py:901
 msgid "Order"
 msgstr ""
 
-#: aleksis/core/models.py:900
+#: aleksis/core/models.py:902
 msgid "Part of the default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:915
+#: aleksis/core/models.py:917
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Dashboard widget order"
 msgstr "Forum"
 
-#: aleksis/core/models.py:916
+#: aleksis/core/models.py:918
 #, fuzzy
 #| msgid "Dashboard"
 msgid "Dashboard widget orders"
 msgstr "Forum"
 
-#: aleksis/core/models.py:922
+#: aleksis/core/models.py:924
 msgid "Menu ID"
 msgstr ""
 
-#: aleksis/core/models.py:935
+#: aleksis/core/models.py:937
 msgid "Custom menu"
 msgstr ""
 
-#: aleksis/core/models.py:936
+#: aleksis/core/models.py:938
 msgid "Custom menus"
 msgstr ""
 
-#: aleksis/core/models.py:946
+#: aleksis/core/models.py:948
 msgid "Menu"
 msgstr ""
 
-#: aleksis/core/models.py:950
+#: aleksis/core/models.py:952
 msgid "Icon"
 msgstr "Nota"
 
-#: aleksis/core/models.py:956
+#: aleksis/core/models.py:958
 msgid "Custom menu item"
 msgstr ""
 
-#: aleksis/core/models.py:957
+#: aleksis/core/models.py:959
 msgid "Custom menu items"
 msgstr ""
 
-#: aleksis/core/models.py:971
+#: aleksis/core/models.py:973
 msgid "Title of type"
 msgstr ""
 
-#: aleksis/core/models.py:978 aleksis/core/templates/core/group/full.html:47
+#: aleksis/core/models.py:980 aleksis/core/templates/core/group/full.html:47
 #, fuzzy
 #| msgid "Group"
 msgid "Group type"
 msgstr "Grex"
 
-#: aleksis/core/models.py:992
+#: aleksis/core/models.py:994
 #, fuzzy
 #| msgid "System status"
 msgid "Can view system status"
 msgstr "Status systemae"
 
-#: aleksis/core/models.py:993
+#: aleksis/core/models.py:995
 #, fuzzy
 #| msgid "Data management"
 msgid "Can manage data"
 msgstr "Adminstratio datarum"
 
-#: aleksis/core/models.py:994
+#: aleksis/core/models.py:996
 #, fuzzy
 #| msgid "Stop impersonation"
 msgid "Can impersonate"
 msgstr "Simulandum aliquem finire"
 
-#: aleksis/core/models.py:995
+#: aleksis/core/models.py:997
 msgid "Can use search"
 msgstr ""
 
-#: aleksis/core/models.py:996
+#: aleksis/core/models.py:998
 msgid "Can change site preferences"
 msgstr ""
 
-#: aleksis/core/models.py:997
+#: aleksis/core/models.py:999
 msgid "Can change person preferences"
 msgstr ""
 
-#: aleksis/core/models.py:998
+#: aleksis/core/models.py:1000
 msgid "Can change group preferences"
 msgstr ""
 
-#: aleksis/core/models.py:999
+#: aleksis/core/models.py:1001
 msgid "Can test PDF generation"
 msgstr ""
 
-#: aleksis/core/models.py:1035
+#: aleksis/core/models.py:1037
 msgid "Related data check task"
 msgstr ""
 
-#: aleksis/core/models.py:1043
+#: aleksis/core/models.py:1045
 msgid "Issue solved"
 msgstr ""
 
-#: aleksis/core/models.py:1044
+#: aleksis/core/models.py:1046
 #, fuzzy
 #| msgid "Notifications"
 msgid "Notification sent"
 msgstr "Nuntii"
 
-#: aleksis/core/models.py:1057
+#: aleksis/core/models.py:1059
 msgid "Data check result"
 msgstr ""
 
-#: aleksis/core/models.py:1058
+#: aleksis/core/models.py:1060
 msgid "Data check results"
 msgstr ""
 
-#: aleksis/core/models.py:1060
+#: aleksis/core/models.py:1062
 msgid "Can run data checks"
 msgstr ""
 
-#: aleksis/core/models.py:1061
+#: aleksis/core/models.py:1063
 msgid "Can solve data check problems"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1070
+#, fuzzy
+#| msgid "E-mail address"
+msgid "E-Mail address"
+msgstr "Inscriptio electronica"
+
+#: aleksis/core/models.py:1094
 msgid "Owner"
 msgstr ""
 
-#: aleksis/core/models.py:1080
+#: aleksis/core/models.py:1098
 msgid "File expires at"
 msgstr ""
 
-#: aleksis/core/models.py:1082
+#: aleksis/core/models.py:1100
 msgid "Generated HTML file"
 msgstr ""
 
-#: aleksis/core/models.py:1084
+#: aleksis/core/models.py:1102
 msgid "Generated PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1091
+#: aleksis/core/models.py:1109
 msgid "PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1092
+#: aleksis/core/models.py:1110
 msgid "PDF files"
 msgstr ""
 
-#: aleksis/core/models.py:1097
+#: aleksis/core/models.py:1115
 msgid "Task result"
 msgstr ""
 
-#: aleksis/core/models.py:1100
+#: aleksis/core/models.py:1118
 msgid "Task user"
 msgstr ""
 
-#: aleksis/core/models.py:1112
+#: aleksis/core/models.py:1130
 msgid "Task user assignment"
 msgstr ""
 
-#: aleksis/core/models.py:1113
+#: aleksis/core/models.py:1131
 msgid "Task user assignments"
 msgstr ""
 
-#: aleksis/core/models.py:1127
+#: aleksis/core/models.py:1147
+#, fuzzy
+#| msgid "Additional name(s)"
+msgid "Additional attributes"
+msgstr "addita nomines"
+
+#: aleksis/core/models.py:1185
 msgid "Allowed scopes that clients can request"
 msgstr ""
 
-#: aleksis/core/preferences.py:23
+#: aleksis/core/preferences.py:24
 msgid "General"
 msgstr ""
 
-#: aleksis/core/preferences.py:24
+#: aleksis/core/preferences.py:25
 msgid "School"
 msgstr "Scola"
 
-#: aleksis/core/preferences.py:25
+#: aleksis/core/preferences.py:26
 msgid "Theme"
 msgstr ""
 
-#: aleksis/core/preferences.py:26
+#: aleksis/core/preferences.py:27
 msgid "Mail"
 msgstr ""
 
-#: aleksis/core/preferences.py:28
+#: aleksis/core/preferences.py:29
 msgid "Footer"
 msgstr ""
 
-#: aleksis/core/preferences.py:29
+#: aleksis/core/preferences.py:30
 #, fuzzy
 #| msgid "Data management"
 msgid "Accounts"
 msgstr "Adminstratio datarum"
 
-#: aleksis/core/preferences.py:30
+#: aleksis/core/preferences.py:31
 #, fuzzy
 #| msgid "Notifications"
 msgid "Authentication"
 msgstr "Nuntii"
 
-#: aleksis/core/preferences.py:31
+#: aleksis/core/preferences.py:32
 #, fuzzy
 #| msgid "Impersonation"
 msgid "Internationalisation"
 msgstr "Simulare aliquem"
 
-#: aleksis/core/preferences.py:42
+#: aleksis/core/preferences.py:43
 msgid "Site title"
 msgstr "Titulus paginae"
 
-#: aleksis/core/preferences.py:53
+#: aleksis/core/preferences.py:54
 msgid "Site description"
 msgstr "Descriptio paginae"
 
-#: aleksis/core/preferences.py:64
+#: aleksis/core/preferences.py:65
 msgid "Primary colour"
 msgstr ""
 
-#: aleksis/core/preferences.py:76
+#: aleksis/core/preferences.py:77
 msgid "Secondary colour"
 msgstr ""
 
-#: aleksis/core/preferences.py:87
+#: aleksis/core/preferences.py:88
 #, fuzzy
 #| msgid "Logout"
 msgid "Logo"
 msgstr "nomen retractare"
 
-#: aleksis/core/preferences.py:97
+#: aleksis/core/preferences.py:98
 msgid "Favicon"
 msgstr ""
 
-#: aleksis/core/preferences.py:107
+#: aleksis/core/preferences.py:108
 #, fuzzy
 #| msgid "Icon"
 msgid "PWA-Icon"
 msgstr "Nota"
 
-#: aleksis/core/preferences.py:118
+#: aleksis/core/preferences.py:119
 #, fuzzy
 #| msgid "Last name"
 msgid "Mail out name"
 msgstr "Secondus nomen"
 
-#: aleksis/core/preferences.py:129
+#: aleksis/core/preferences.py:130
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Mail out address"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/preferences.py:141
+#: aleksis/core/preferences.py:142
 msgid "Link to privacy policy"
 msgstr ""
 
-#: aleksis/core/preferences.py:153
+#: aleksis/core/preferences.py:154
 msgid "Link to imprint"
 msgstr ""
 
-#: aleksis/core/preferences.py:165
+#: aleksis/core/preferences.py:166
 msgid "Name format for addressing"
 msgstr ""
 
-#: aleksis/core/preferences.py:181
+#: aleksis/core/preferences.py:182
 msgid "Channels to use for notifications"
 msgstr ""
 
-#: aleksis/core/preferences.py:193
+#: aleksis/core/preferences.py:194
 msgid "Regular expression to match primary group, e.g. '^Class .*'"
 msgstr ""
 
-#: aleksis/core/preferences.py:204
+#: aleksis/core/preferences.py:205
 msgid "Field on person to match primary group against"
 msgstr ""
 
-#: aleksis/core/preferences.py:216
+#: aleksis/core/preferences.py:217
 msgid "Automatically create new persons for new users"
 msgstr ""
 
-#: aleksis/core/preferences.py:225
+#: aleksis/core/preferences.py:226
 msgid "Automatically link existing persons to new users by their e-mail address"
 msgstr ""
 
-#: aleksis/core/preferences.py:236
+#: aleksis/core/preferences.py:237
 msgid "Display name of the school"
 msgstr ""
 
-#: aleksis/core/preferences.py:247
+#: aleksis/core/preferences.py:248
 msgid "Official name of the school, e.g. as given by supervisory authority"
 msgstr "Officialis nomen scolae, e. g."
 
-#: aleksis/core/preferences.py:255
+#: aleksis/core/preferences.py:256
 msgid "Allow users to change their passwords"
 msgstr ""
 
-#: aleksis/core/preferences.py:263
+#: aleksis/core/preferences.py:264
 msgid "Enable signup"
 msgstr ""
 
-#: aleksis/core/preferences.py:274
+#: aleksis/core/preferences.py:272
+msgid "Enable invitations"
+msgstr ""
+
+#: aleksis/core/preferences.py:280
+msgid "Length of invite code. (Default 3: abcde-acbde-abcde)"
+msgstr ""
+
+#: aleksis/core/preferences.py:288
+msgid "Size of packets. (Default 5: abcde)"
+msgstr ""
+
+#: aleksis/core/preferences.py:298
 #, fuzzy
 #| msgid "E-mail address"
 msgid "Allowed Grant Flows for OAuth applications"
 msgstr "Inscriptio electronica"
 
-#: aleksis/core/preferences.py:287
+#: aleksis/core/preferences.py:311
 msgid "Available languages"
 msgstr ""
 
-#: aleksis/core/preferences.py:299
+#: aleksis/core/preferences.py:323
 msgid "Send emails if data checks detect problems"
 msgstr ""
 
-#: aleksis/core/preferences.py:310
+#: aleksis/core/preferences.py:334
 msgid "Email recipients for data checks problem emails"
 msgstr ""
 
-#: aleksis/core/preferences.py:321
+#: aleksis/core/preferences.py:345
 msgid "Email recipient groups for data checks problem emails"
 msgstr ""
 
-#: aleksis/core/preferences.py:330
+#: aleksis/core/preferences.py:354
 msgid "Show dashboard to users without login"
 msgstr ""
 
-#: aleksis/core/preferences.py:339
+#: aleksis/core/preferences.py:363
 msgid "Allow users to edit their dashboard"
 msgstr ""
 
-#: aleksis/core/preferences.py:350
+#: aleksis/core/preferences.py:374
 msgid "Fields on person model which are editable by themselves."
 msgstr ""
 
-#: aleksis/core/preferences.py:364
+#: aleksis/core/preferences.py:388
 msgid "Editable fields on person model which should trigger a notification on change"
 msgstr ""
 
-#: aleksis/core/preferences.py:377
+#: aleksis/core/preferences.py:401
 msgid "Contact for notification if a person changes their data"
 msgstr ""
 
-#: aleksis/core/preferences.py:387
+#: aleksis/core/preferences.py:411
 msgid "PDF file expiration duration"
 msgstr ""
 
-#: aleksis/core/preferences.py:388
+#: aleksis/core/preferences.py:412
 msgid "in minutes"
 msgstr ""
 
-#: aleksis/core/preferences.py:398
+#: aleksis/core/preferences.py:422
 msgid "Automatically update the dashboard and its widgets"
 msgstr ""
 
-#: aleksis/core/preferences.py:408
+#: aleksis/core/preferences.py:432
 msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr ""
 
-#: aleksis/core/settings.py:474
+#: aleksis/core/settings.py:507
 msgid "English"
 msgstr "Britannicus"
 
-#: aleksis/core/settings.py:475
+#: aleksis/core/settings.py:508
 msgid "German"
 msgstr "Germanus"
 
-#: aleksis/core/tables.py:19
+#: aleksis/core/tables.py:24
 #: aleksis/core/templates/core/announcement/list.html:36
 #: aleksis/core/templates/core/group/full.html:24
-#: aleksis/core/templates/core/person/full.html:23
+#: aleksis/core/templates/core/person/full.html:24
 #: aleksis/core/templates/oauth2_provider/application/detail.html:17
 msgid "Edit"
 msgstr ""
 
-#: aleksis/core/tables.py:21 aleksis/core/tables.py:89
-#: aleksis/core/tables.py:105
+#: aleksis/core/tables.py:26 aleksis/core/tables.py:94
+#: aleksis/core/tables.py:137
 #: aleksis/core/templates/core/announcement/list.html:22
 #, fuzzy
 #| msgid "Notifications"
 msgid "Actions"
 msgstr "Nuntii"
 
-#: aleksis/core/tables.py:56 aleksis/core/tables.py:57
-#: aleksis/core/tables.py:71 aleksis/core/tables.py:87
-#: aleksis/core/tables.py:103
+#: aleksis/core/tables.py:61 aleksis/core/tables.py:62
+#: aleksis/core/tables.py:76 aleksis/core/tables.py:92
+#: aleksis/core/tables.py:135
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/group/full.html:31
 #: aleksis/core/templates/core/pages/delete.html:22
-#: aleksis/core/templates/core/person/full.html:30
+#: aleksis/core/templates/core/person/full.html:31
 #: aleksis/core/templates/oauth2_provider/application/detail.html:21
 msgid "Delete"
 msgstr ""
@@ -1328,11 +1357,11 @@ msgstr ""
 msgid "Account inactive"
 msgstr ""
 
-#: aleksis/core/templates/account/account_inactive.html:13
+#: aleksis/core/templates/account/account_inactive.html:14
 msgid "Account inactive."
 msgstr ""
 
-#: aleksis/core/templates/account/account_inactive.html:15
+#: aleksis/core/templates/account/account_inactive.html:17
 msgid ""
 "\n"
 "            This account is currently inactive. If you think this is an\n"
@@ -1486,11 +1515,11 @@ msgstr ""
 msgid "Signup closed"
 msgstr ""
 
-#: aleksis/core/templates/account/signup_closed.html:13
+#: aleksis/core/templates/account/signup_closed.html:14
 msgid "Signup closed."
 msgstr ""
 
-#: aleksis/core/templates/account/signup_closed.html:15
+#: aleksis/core/templates/account/signup_closed.html:17
 msgid ""
 "\n"
 "            This sign up is currently closed. If you think this is an\n"
@@ -1530,11 +1559,6 @@ msgid ""
 "          "
 msgstr ""
 
-#: aleksis/core/templates/account/verification_sent.html:30
-#, python-format
-msgid "<strong>Note:</strong> you can still <a href=\"%(email_url)s\">change your e-mail address</a>"
-msgstr ""
-
 #: aleksis/core/templates/core/additional_field/edit.html:6
 #: aleksis/core/templates/core/additional_field/edit.html:7
 #, fuzzy
@@ -1597,11 +1621,11 @@ msgid "Logged in as"
 msgstr ""
 
 #: aleksis/core/templates/core/base.html:175
-msgid "About AlekSIS — The Free School Information System"
+msgid "About AlekSIS® — The Free School Information System"
 msgstr ""
 
 #: aleksis/core/templates/core/base.html:183
-msgid "Impress"
+msgid "Imprint"
 msgstr ""
 
 #: aleksis/core/templates/core/base.html:191
@@ -1609,7 +1633,7 @@ msgid "Privacy Policy"
 msgstr ""
 
 #: aleksis/core/templates/core/base_print.html:72
-msgid "Powered by AlekSIS"
+msgid "Powered by AlekSIS®"
 msgstr ""
 
 #: aleksis/core/templates/core/dashboard_widget/create.html:8
@@ -1838,7 +1862,7 @@ msgid "Edit group"
 msgstr ""
 
 #: aleksis/core/templates/core/group/full.html:38
-#: aleksis/core/templates/core/person/full.html:37
+#: aleksis/core/templates/core/person/full.html:38
 msgid "Change preferences"
 msgstr ""
 
@@ -1937,38 +1961,48 @@ msgid "No notifications available yet."
 msgstr ""
 
 #: aleksis/core/templates/core/pages/about.html:6
-#: aleksis/core/templates/core/pages/about.html:15
-msgid "About AlekSIS"
+msgid "About AlekSIS®"
 msgstr ""
 
 #: aleksis/core/templates/core/pages/about.html:7
-msgid "AlekSIS – The Free School Information System"
+msgid "AlekSIS® – The Free School Information System"
+msgstr ""
+
+#: aleksis/core/templates/core/pages/about.html:15
+msgid "About AlekSIS"
 msgstr ""
 
 #: aleksis/core/templates/core/pages/about.html:17
 msgid ""
 "\n"
-"              This platform is powered by AlekSIS, a web-based school information system (SIS) which can be used\n"
+"              This platform is powered by AlekSIS®, a web-based school information system (SIS) which can be used\n"
 "              to manage and/or publish organisational artifacts of educational institutions. AlekSIS is free software and\n"
 "              can be used by anyone.\n"
 "            "
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:25
+#: aleksis/core/templates/core/pages/about.html:24
+msgid ""
+"\n"
+"              AlekSIS® is a registered trademark of the AlekSIS open source project, represented by Teckids e.V.\n"
+"            "
+msgstr ""
+
+#: aleksis/core/templates/core/pages/about.html:30
 msgid "Website of AlekSIS"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:26
+#: aleksis/core/templates/core/pages/about.html:31
 msgid "Source code"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:35
+#: aleksis/core/templates/core/pages/about.html:40
 #, fuzzy
 #| msgid "Edit school information"
 msgid "Licence information"
 msgstr "Muta informationes scolae"
 
-#: aleksis/core/templates/core/pages/about.html:37
+#: aleksis/core/templates/core/pages/about.html:42
 msgid ""
 "\n"
 "              The core and the official apps of AlekSIS are licenced under the EUPL, version 1.2 or later. For licence\n"
@@ -1977,25 +2011,25 @@ msgid ""
 "            "
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:45
+#: aleksis/core/templates/core/pages/about.html:50
 msgid "Free/Open Source Licence"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:46
+#: aleksis/core/templates/core/pages/about.html:51
 msgid "Other Licence"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:50
+#: aleksis/core/templates/core/pages/about.html:55
 msgid "Full licence text"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:51
+#: aleksis/core/templates/core/pages/about.html:56
 #, fuzzy
 #| msgid "Edit school information"
 msgid "More information about the EUPL"
 msgstr "Muta informationes scolae"
 
-#: aleksis/core/templates/core/pages/about.html:90
+#: aleksis/core/templates/core/pages/about.html:95
 #, python-format
 msgid ""
 "\n"
@@ -2248,19 +2282,23 @@ msgstr "Simulandum aliquem finire"
 msgid "Edit person"
 msgstr ""
 
-#: aleksis/core/templates/core/person/full.html:44
-#: aleksis/core/templates/impersonate/list_users.html:7
-#: aleksis/core/templates/impersonate/list_users.html:8
+#: aleksis/core/templates/core/person/full.html:45
 #, fuzzy
 #| msgid "Impersonation"
 msgid "Impersonate"
 msgstr "Simulare aliquem"
 
-#: aleksis/core/templates/core/person/full.html:50
+#: aleksis/core/templates/core/person/full.html:51
+#, fuzzy
+#| msgid "Impersonation"
+msgid "Invite user"
+msgstr "Simulare aliquem"
+
+#: aleksis/core/templates/core/person/full.html:57
 msgid "Contact details"
 msgstr ""
 
-#: aleksis/core/templates/core/person/full.html:131
+#: aleksis/core/templates/core/person/full.html:138
 msgid "Children"
 msgstr ""
 
@@ -2302,6 +2340,51 @@ msgstr ""
 msgid "Save preferences"
 msgstr ""
 
+#: aleksis/core/templates/invitations/enter.html:21
+msgid "Accept your invitation"
+msgstr ""
+
+#: aleksis/core/templates/invitations/enter.html:25
+msgid ""
+"\n"
+"                Please enter your invitation code to register\n"
+"                your new user account:\n"
+"              "
+msgstr ""
+
+#: aleksis/core/templates/invitations/enter.html:37
+msgid "Accept invite"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:9
+#: aleksis/core/templates/invitations/forms/_invite.html:10
+#: aleksis/core/templates/invitations/forms/_invite.html:21
+msgid "Invite"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:17
+#, fuzzy
+#| msgid "E-mail address"
+msgid "Invite by email address"
+msgstr "Inscriptio electronica"
+
+#: aleksis/core/templates/invitations/forms/_invite.html:26
+msgid "Generate invitation code"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:29
+msgid "Generate code"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:33
+msgid "Invitations"
+msgstr ""
+
+#: aleksis/core/templates/invitations/messages/invite_accepted.txt:3
+#, python-format
+msgid "The invitation for %(email)s has been accepted."
+msgstr ""
+
 #: aleksis/core/templates/oauth2_provider/application/create.html:5
 #: aleksis/core/templates/oauth2_provider/application/create.html:6
 #, fuzzy
@@ -2981,153 +3064,169 @@ msgstr ""
 msgid "SMS"
 msgstr ""
 
-#: aleksis/core/util/pdf.py:105
+#: aleksis/core/util/pdf.py:113
 msgid "Progress: Generate PDF file"
 msgstr ""
 
-#: aleksis/core/util/pdf.py:106
+#: aleksis/core/util/pdf.py:114
 msgid "Generating PDF file …"
 msgstr ""
 
-#: aleksis/core/util/pdf.py:107
+#: aleksis/core/util/pdf.py:115
 msgid "The PDF file has been generated successfully."
 msgstr ""
 
-#: aleksis/core/util/pdf.py:108
+#: aleksis/core/util/pdf.py:116
 msgid "There was a problem while generating the PDF file."
 msgstr ""
 
-#: aleksis/core/util/pdf.py:111
+#: aleksis/core/util/pdf.py:119
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/core/views.py:270
+#: aleksis/core/views.py:280
 msgid "The school term has been created."
 msgstr ""
 
-#: aleksis/core/views.py:282
+#: aleksis/core/views.py:292
 msgid "The school term has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:406
+#: aleksis/core/views.py:416
 msgid "The child groups were successfully saved."
 msgstr ""
 
-#: aleksis/core/views.py:425 aleksis/core/views.py:435
+#: aleksis/core/views.py:435 aleksis/core/views.py:445
 msgid "The person has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:485
+#: aleksis/core/views.py:495
 msgid "The group has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:582
+#: aleksis/core/views.py:592
 msgid "The announcement has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:598
+#: aleksis/core/views.py:608
 msgid "The announcement has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:682
+#: aleksis/core/views.py:695
 msgid "The preferences have been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:706
+#: aleksis/core/views.py:719
 msgid "The person has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:720
+#: aleksis/core/views.py:733
 msgid "The group has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:752
+#: aleksis/core/views.py:765
 msgid "The additional_field has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:786
+#: aleksis/core/views.py:799
 msgid "The additional field has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:811
+#: aleksis/core/views.py:824
 msgid "The group type has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:841
+#: aleksis/core/views.py:854
 msgid "The group type has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:874
+#: aleksis/core/views.py:887
 msgid "Progress: Run data checks"
 msgstr ""
 
-#: aleksis/core/views.py:875
+#: aleksis/core/views.py:888
 #, fuzzy
 #| msgid "System status"
 msgid "Run data checks …"
 msgstr "Status systemae"
 
-#: aleksis/core/views.py:876
+#: aleksis/core/views.py:889
 msgid "The data checks were run successfully."
 msgstr ""
 
-#: aleksis/core/views.py:877
+#: aleksis/core/views.py:890
 msgid "There was a problem while running data checks."
 msgstr ""
 
-#: aleksis/core/views.py:893
+#: aleksis/core/views.py:906
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr ""
 
-#: aleksis/core/views.py:935
+#: aleksis/core/views.py:948
 msgid "The dashboard widget has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:965
+#: aleksis/core/views.py:978
 msgid "The dashboard widget has been created."
 msgstr ""
 
-#: aleksis/core/views.py:975
+#: aleksis/core/views.py:988
 msgid "The dashboard widget has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1042
+#: aleksis/core/views.py:1055
 msgid "Your dashboard configuration has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1044
+#: aleksis/core/views.py:1057
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1139
+#: aleksis/core/views.py:1127
+#, python-brace-format
+msgid "The invitation was successfully created. The invitation code is {code}"
+msgstr ""
+
+#: aleksis/core/views.py:1218
 msgid "We have successfully assigned the permissions."
 msgstr ""
 
-#: aleksis/core/views.py:1149
+#: aleksis/core/views.py:1228
 msgid "The global user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1159
+#: aleksis/core/views.py:1238
 msgid "The global group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1169
+#: aleksis/core/views.py:1248
 msgid "The object user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1179
+#: aleksis/core/views.py:1258
 msgid "The object group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1298
+#: aleksis/core/views.py:1377
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr ""
 
-#: aleksis/core/views.py:1305
+#: aleksis/core/views.py:1384
 msgid "The third-party account has been successfully disconnected."
 msgstr ""
 
+#: aleksis/core/views.py:1441
+msgid "Person was invited successfully."
+msgstr ""
+
+#: aleksis/core/views.py:1443
+msgid "Person was already invited."
+msgstr ""
+
+#~ msgid "Impersonation"
+#~ msgstr "Simulare aliquem"
+
 #, fuzzy
 #~| msgid "Notifications"
 #~ msgid "OAuth2 applications"
@@ -3146,11 +3245,6 @@ msgstr ""
 #~ msgid "Link persons to accounts"
 #~ msgstr "Personae et computi"
 
-#, fuzzy
-#~| msgid "Impersonation"
-#~ msgid "Impersonate user"
-#~ msgstr "Simulare aliquem"
-
 #~ msgid "School logo"
 #~ msgstr "Imago scolae"
 
diff --git a/aleksis/core/locale/la/LC_MESSAGES/djangojs.po b/aleksis/core/locale/la/LC_MESSAGES/djangojs.po
index 2458849847f712fe1cf63743640716c5b3ef561a..d67bdb0055bf1ee96915909ce9e21023085c466b 100644
--- a/aleksis/core/locale/la/LC_MESSAGES/djangojs.po
+++ b/aleksis/core/locale/la/LC_MESSAGES/djangojs.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-11-29 09:59+0100\n"
+"POT-Creation-Date: 2021-12-28 12:14+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -29,6 +29,6 @@ msgstr ""
 msgid "OK"
 msgstr ""
 
-#: aleksis/core/static/js/main.js:127
+#: aleksis/core/static/js/main.js:128
 msgid "This page may contain outdated information since there is no internet connection."
 msgstr ""
diff --git a/aleksis/core/locale/nb_NO/LC_MESSAGES/django.po b/aleksis/core/locale/nb_NO/LC_MESSAGES/django.po
index cf83adad715c4a2b0d4b804801f281e508de8367..e40c13ef84618ccb0eb3bd58c524f81e00fe8834 100644
--- a/aleksis/core/locale/nb_NO/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/nb_NO/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: AlekSIS (School Information System) 0.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-11-29 09:59+0100\n"
+"POT-Creation-Date: 2021-12-28 12:13+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,26 +17,33 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: aleksis/core/apps.py:151
+#: aleksis/core/apps.py:152
 msgid "OpenID Connect scope"
 msgstr ""
 
-#: aleksis/core/apps.py:152
+#: aleksis/core/apps.py:153
 msgid "Given name, family name, link to profile and picture if existing."
 msgstr ""
 
-#: aleksis/core/apps.py:153
+#: aleksis/core/apps.py:154
 msgid "Full home postal address"
 msgstr ""
 
-#: aleksis/core/apps.py:154
+#: aleksis/core/apps.py:155
 msgid "Email address"
 msgstr ""
 
-#: aleksis/core/apps.py:155
+#: aleksis/core/apps.py:156
 msgid "Home and mobile phone"
 msgstr ""
 
+#: aleksis/core/apps.py:157 aleksis/core/forms.py:223 aleksis/core/menus.py:265
+#: aleksis/core/models.py:414 aleksis/core/templates/core/group/list.html:8
+#: aleksis/core/templates/core/group/list.html:9
+#: aleksis/core/templates/core/person/full.html:152
+msgid "Groups"
+msgstr ""
+
 #: aleksis/core/data_checks.py:55
 msgid "Ignore problem"
 msgstr ""
@@ -83,156 +90,148 @@ msgstr ""
 msgid "Content type"
 msgstr ""
 
-#: aleksis/core/filters.py:112 aleksis/core/models.py:579
+#: aleksis/core/filters.py:112 aleksis/core/models.py:581
 msgid "User"
 msgstr ""
 
-#: aleksis/core/filters.py:134 aleksis/core/models.py:411
+#: aleksis/core/filters.py:134 aleksis/core/models.py:413
 msgid "Group"
 msgstr ""
 
-#: aleksis/core/forms.py:46 aleksis/core/forms.py:514
+#: aleksis/core/forms.py:48 aleksis/core/forms.py:559
 msgid "Base data"
 msgstr ""
 
-#: aleksis/core/forms.py:52
+#: aleksis/core/forms.py:54
 msgid "Address"
 msgstr ""
 
-#: aleksis/core/forms.py:53
+#: aleksis/core/forms.py:55 aleksis/core/forms.py:568
 msgid "Contact data"
 msgstr ""
 
-#: aleksis/core/forms.py:55
+#: aleksis/core/forms.py:57
 msgid "Advanced personal data"
 msgstr ""
 
-#: aleksis/core/forms.py:103
+#: aleksis/core/forms.py:105
 msgid "New user"
 msgstr ""
 
-#: aleksis/core/forms.py:103
+#: aleksis/core/forms.py:105
 msgid "Create a new account"
 msgstr ""
 
-#: aleksis/core/forms.py:134
+#: aleksis/core/forms.py:136
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr ""
 
-#: aleksis/core/forms.py:138
+#: aleksis/core/forms.py:140
 msgid "This username is already in use."
 msgstr ""
 
-#: aleksis/core/forms.py:155 aleksis/core/models.py:125
+#: aleksis/core/forms.py:157 aleksis/core/models.py:129
 msgid "School term"
 msgstr ""
 
-#: aleksis/core/forms.py:156
+#: aleksis/core/forms.py:158
 msgid "Common data"
 msgstr ""
 
-#: aleksis/core/forms.py:157 aleksis/core/forms.py:208
-#: aleksis/core/menus.py:256 aleksis/core/models.py:148
+#: aleksis/core/forms.py:159 aleksis/core/forms.py:210
+#: aleksis/core/menus.py:254 aleksis/core/models.py:152
 #: aleksis/core/templates/core/person/list.html:8
 #: aleksis/core/templates/core/person/list.html:9
 msgid "Persons"
 msgstr ""
 
-#: aleksis/core/forms.py:158
+#: aleksis/core/forms.py:160 aleksis/core/forms.py:570
 msgid "Additional data"
 msgstr ""
 
-#: aleksis/core/forms.py:200 aleksis/core/forms.py:203
-#: aleksis/core/models.py:68
+#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
+#: aleksis/core/models.py:72
 msgid "Date"
 msgstr ""
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:76
+#: aleksis/core/forms.py:203 aleksis/core/forms.py:206
+#: aleksis/core/models.py:80
 msgid "Time"
 msgstr ""
 
-#: aleksis/core/forms.py:221 aleksis/core/menus.py:267
-#: aleksis/core/models.py:412 aleksis/core/templates/core/group/list.html:8
-#: aleksis/core/templates/core/group/list.html:9
-#: aleksis/core/templates/core/person/full.html:145
-msgid "Groups"
-msgstr ""
-
-#: aleksis/core/forms.py:234
+#: aleksis/core/forms.py:236
 msgid "From when until when should the announcement be displayed?"
 msgstr ""
 
-#: aleksis/core/forms.py:237
+#: aleksis/core/forms.py:239
 msgid "Who should see the announcement?"
 msgstr ""
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:240
 msgid "Write your announcement:"
 msgstr ""
 
-#: aleksis/core/forms.py:277
+#: aleksis/core/forms.py:279
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr ""
 
-#: aleksis/core/forms.py:281
+#: aleksis/core/forms.py:283
 msgid "The from date and time must be earlier then the until date and time."
 msgstr ""
 
-#: aleksis/core/forms.py:290
+#: aleksis/core/forms.py:292
 msgid "You need at least one recipient."
 msgstr ""
 
-#: aleksis/core/forms.py:411
+#: aleksis/core/forms.py:401
+msgid "Invitation code"
+msgstr ""
+
+#: aleksis/core/forms.py:402
+msgid "Please enter your invitation code."
+msgstr ""
+
+#: aleksis/core/forms.py:434
 msgid "Who should get the permission?"
 msgstr ""
 
-#: aleksis/core/forms.py:412
+#: aleksis/core/forms.py:435
 msgid "On what?"
 msgstr ""
 
-#: aleksis/core/forms.py:438
+#: aleksis/core/forms.py:461
 msgid "Select objects which the permission should be granted for:"
 msgstr ""
 
-#: aleksis/core/forms.py:441
+#: aleksis/core/forms.py:464
 msgid "Grant the permission for all objects"
 msgstr ""
 
-#: aleksis/core/forms.py:449
+#: aleksis/core/forms.py:472
 msgid "You must select at least one group or person which should get the permission."
 msgstr ""
 
-#: aleksis/core/forms.py:454
+#: aleksis/core/forms.py:477
 msgid "You must grant the permission to all objects and/or to some objects."
 msgstr ""
 
-#: aleksis/core/forms.py:518
-msgid "Account data"
+#: aleksis/core/forms.py:564
+msgid "Adress data"
 msgstr ""
 
-#: aleksis/core/forms.py:524
-msgid "Consents"
+#: aleksis/core/forms.py:576
+msgid "Account data"
 msgstr ""
 
-#: aleksis/core/forms.py:531
+#: aleksis/core/forms.py:583
 msgid "Password"
 msgstr ""
 
-#: aleksis/core/forms.py:537
+#: aleksis/core/forms.py:586
 msgid "Password (again)"
 msgstr ""
 
-#: aleksis/core/forms.py:550
-#, python-brace-format
-msgid "I have read the <a href='{privacy_policy}'>Privacy policy</a> and agree with them."
-msgstr ""
-
-#: aleksis/core/forms.py:575
-msgid "You must type the same password each time."
-msgstr ""
-
-#: aleksis/core/forms.py:720
+#: aleksis/core/forms.py:752
 msgid "No valid selection."
 msgstr ""
 
@@ -240,24 +239,20 @@ msgstr ""
 msgid "There are unresolved data problems."
 msgstr ""
 
-#: aleksis/core/health_checks.py:38
-msgid "The backup folder doesn't exist."
-msgstr ""
-
-#: aleksis/core/health_checks.py:47
+#: aleksis/core/health_checks.py:44
 #, python-brace-format
 msgid "Last backup {time_gone_since_backup}!"
 msgstr ""
 
-#: aleksis/core/health_checks.py:49
+#: aleksis/core/health_checks.py:46
 msgid "No backup found!"
 msgstr ""
 
-#: aleksis/core/health_checks.py:76
+#: aleksis/core/health_checks.py:73
 msgid "No backup result found!"
 msgstr ""
 
-#: aleksis/core/health_checks.py:78
+#: aleksis/core/health_checks.py:75
 #, python-brace-format
 msgid "{task.status} - {task.result}"
 msgstr ""
@@ -273,34 +268,38 @@ msgstr ""
 msgid "Sign up"
 msgstr ""
 
-#: aleksis/core/menus.py:24
+#: aleksis/core/menus.py:24 aleksis/core/templates/invitations/enter.html:7
+msgid "Accept invitation"
+msgstr ""
+
+#: aleksis/core/menus.py:33
 msgid "Dashboard"
 msgstr ""
 
-#: aleksis/core/menus.py:32 aleksis/core/models.py:625
-#: aleksis/core/preferences.py:27
+#: aleksis/core/menus.py:41 aleksis/core/models.py:627
+#: aleksis/core/preferences.py:28
 #: aleksis/core/templates/core/notifications.html:4
 #: aleksis/core/templates/core/notifications.html:5
 msgid "Notifications"
 msgstr ""
 
-#: aleksis/core/menus.py:44
+#: aleksis/core/menus.py:53
 msgid "Account"
 msgstr ""
 
-#: aleksis/core/menus.py:51
+#: aleksis/core/menus.py:60
 msgid "Stop impersonation"
 msgstr ""
 
-#: aleksis/core/menus.py:60 aleksis/core/templates/core/base.html:80
+#: aleksis/core/menus.py:69 aleksis/core/templates/core/base.html:80
 msgid "Logout"
 msgstr ""
 
-#: aleksis/core/menus.py:66
+#: aleksis/core/menus.py:75
 msgid "2FA"
 msgstr ""
 
-#: aleksis/core/menus.py:74
+#: aleksis/core/menus.py:83
 #: aleksis/core/templates/account/password_change.html:5
 #: aleksis/core/templates/account/password_change.html:6
 #: aleksis/core/templates/account/password_change.html:19
@@ -312,830 +311,850 @@ msgstr ""
 msgid "Change password"
 msgstr ""
 
-#: aleksis/core/menus.py:86
+#: aleksis/core/menus.py:95
 msgid "Me"
 msgstr ""
 
-#: aleksis/core/menus.py:95
+#: aleksis/core/menus.py:104
 #: aleksis/core/templates/dynamic_preferences/form.html:5
 msgid "Preferences"
 msgstr ""
 
-#: aleksis/core/menus.py:104
+#: aleksis/core/menus.py:113
 msgid "Third-party accounts"
 msgstr ""
 
-#: aleksis/core/menus.py:113
+#: aleksis/core/menus.py:122
 #: aleksis/core/templates/oauth2_provider/authorized-tokens.html:5
 #: aleksis/core/templates/oauth2_provider/authorized-tokens.html:6
 msgid "Authorized applications"
 msgstr ""
 
-#: aleksis/core/menus.py:124
+#: aleksis/core/menus.py:133
 msgid "Admin"
 msgstr ""
 
-#: aleksis/core/menus.py:132 aleksis/core/models.py:725
+#: aleksis/core/menus.py:141 aleksis/core/models.py:727
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr ""
 
-#: aleksis/core/menus.py:143 aleksis/core/models.py:126
+#: aleksis/core/menus.py:152 aleksis/core/models.py:130
 #: aleksis/core/templates/core/school_term/list.html:8
 #: aleksis/core/templates/core/school_term/list.html:9
 msgid "School terms"
 msgstr ""
 
-#: aleksis/core/menus.py:154
+#: aleksis/core/menus.py:163
 #: aleksis/core/templates/core/dashboard_widget/list.html:8
 #: aleksis/core/templates/core/dashboard_widget/list.html:9
 msgid "Dashboard widgets"
 msgstr ""
 
-#: aleksis/core/menus.py:165
+#: aleksis/core/menus.py:174
 #: aleksis/core/templates/core/management/data_management.html:6
 #: aleksis/core/templates/core/management/data_management.html:7
 msgid "Data management"
 msgstr ""
 
-#: aleksis/core/menus.py:176
+#: aleksis/core/menus.py:185
 #: aleksis/core/templates/core/pages/system_status.html:5
 #: aleksis/core/templates/core/pages/system_status.html:7
 msgid "System status"
 msgstr ""
 
-#: aleksis/core/menus.py:187
-msgid "Impersonation"
-msgstr ""
-
-#: aleksis/core/menus.py:198
+#: aleksis/core/menus.py:196
 msgid "Configuration"
 msgstr ""
 
-#: aleksis/core/menus.py:209 aleksis/core/templates/core/data_check/list.html:9
+#: aleksis/core/menus.py:207 aleksis/core/templates/core/data_check/list.html:9
 #: aleksis/core/templates/core/data_check/list.html:10
 msgid "Data checks"
 msgstr ""
 
-#: aleksis/core/menus.py:215 aleksis/core/templates/core/perms/list.html:13
+#: aleksis/core/menus.py:213 aleksis/core/templates/core/perms/list.html:13
 #: aleksis/core/templates/core/perms/list.html:14
 msgid "Manage permissions"
 msgstr ""
 
-#: aleksis/core/menus.py:226
+#: aleksis/core/menus.py:224
 msgid "Backend Admin"
 msgstr ""
 
-#: aleksis/core/menus.py:234
+#: aleksis/core/menus.py:232
 #: aleksis/core/templates/oauth2_provider/application/list.html:5
 #: aleksis/core/templates/oauth2_provider/application/list.html:6
 msgid "OAuth2 Applications"
 msgstr ""
 
-#: aleksis/core/menus.py:247
+#: aleksis/core/menus.py:245
 msgid "People"
 msgstr ""
 
-#: aleksis/core/menus.py:278 aleksis/core/models.py:979
+#: aleksis/core/menus.py:276 aleksis/core/models.py:981
 #: aleksis/core/templates/core/group_type/list.html:8
 #: aleksis/core/templates/core/group_type/list.html:9
 msgid "Group types"
 msgstr ""
 
-#: aleksis/core/menus.py:289
+#: aleksis/core/menus.py:287
 msgid "Groups and child groups"
 msgstr ""
 
-#: aleksis/core/menus.py:300 aleksis/core/models.py:460
+#: aleksis/core/menus.py:298 aleksis/core/models.py:462
 #: aleksis/core/templates/core/additional_field/list.html:8
 #: aleksis/core/templates/core/additional_field/list.html:9
 msgid "Additional fields"
 msgstr ""
 
-#: aleksis/core/menus.py:315
+#: aleksis/core/menus.py:309
+msgid "Invite person"
+msgstr ""
+
+#: aleksis/core/menus.py:322
 #: aleksis/core/templates/core/group/child_groups.html:7
 #: aleksis/core/templates/core/group/child_groups.html:9
 msgid "Assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/mixins.py:508
+#: aleksis/core/mixins.py:511
 msgid "Linked school term"
 msgstr ""
 
-#: aleksis/core/models.py:66
+#: aleksis/core/models.py:70
 msgid "Boolean (Yes/No)"
 msgstr ""
 
-#: aleksis/core/models.py:67
+#: aleksis/core/models.py:71
 msgid "Text (one line)"
 msgstr ""
 
-#: aleksis/core/models.py:69
+#: aleksis/core/models.py:73
 msgid "Date and time"
 msgstr ""
 
-#: aleksis/core/models.py:70
+#: aleksis/core/models.py:74
 msgid "Decimal number"
 msgstr ""
 
-#: aleksis/core/models.py:71 aleksis/core/models.py:194
+#: aleksis/core/models.py:75 aleksis/core/models.py:198
 msgid "E-mail address"
 msgstr ""
 
-#: aleksis/core/models.py:72
+#: aleksis/core/models.py:76
 msgid "Integer"
 msgstr ""
 
-#: aleksis/core/models.py:73
+#: aleksis/core/models.py:77
 msgid "IP address"
 msgstr ""
 
-#: aleksis/core/models.py:74
+#: aleksis/core/models.py:78
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr ""
 
-#: aleksis/core/models.py:75
+#: aleksis/core/models.py:79
 msgid "Text (multi-line)"
 msgstr ""
 
-#: aleksis/core/models.py:77
+#: aleksis/core/models.py:81
 msgid "URL / Link"
 msgstr ""
 
-#: aleksis/core/models.py:89 aleksis/core/models.py:948
+#: aleksis/core/models.py:93 aleksis/core/models.py:950
 msgid "Name"
 msgstr ""
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:95
 msgid "Start date"
 msgstr ""
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:96
 msgid "End date"
 msgstr ""
 
-#: aleksis/core/models.py:111
+#: aleksis/core/models.py:115
 msgid "The start date must be earlier than the end date."
 msgstr ""
 
-#: aleksis/core/models.py:118
+#: aleksis/core/models.py:122
 msgid "There is already a school term for this time or a part of this time."
 msgstr ""
 
-#: aleksis/core/models.py:147 aleksis/core/models.py:897
+#: aleksis/core/models.py:151 aleksis/core/models.py:899
 msgid "Person"
 msgstr ""
 
-#: aleksis/core/models.py:150
+#: aleksis/core/models.py:154
 msgid "Can view address"
 msgstr ""
 
-#: aleksis/core/models.py:151
+#: aleksis/core/models.py:155
 msgid "Can view contact details"
 msgstr ""
 
-#: aleksis/core/models.py:152
+#: aleksis/core/models.py:156
 msgid "Can view photo"
 msgstr ""
 
-#: aleksis/core/models.py:153
+#: aleksis/core/models.py:157
 msgid "Can view persons groups"
 msgstr ""
 
-#: aleksis/core/models.py:154
+#: aleksis/core/models.py:158
 msgid "Can view personal details"
 msgstr ""
 
-#: aleksis/core/models.py:164
+#: aleksis/core/models.py:168
 msgid "female"
 msgstr ""
 
-#: aleksis/core/models.py:164
+#: aleksis/core/models.py:168
 msgid "male"
 msgstr ""
 
-#: aleksis/core/models.py:172
+#: aleksis/core/models.py:176 aleksis/core/models.py:1144
 msgid "Linked user"
 msgstr ""
 
-#: aleksis/core/models.py:174
+#: aleksis/core/models.py:178
 msgid "Is person active?"
 msgstr ""
 
-#: aleksis/core/models.py:176
+#: aleksis/core/models.py:180
 msgid "First name"
 msgstr ""
 
-#: aleksis/core/models.py:177
+#: aleksis/core/models.py:181
 msgid "Last name"
 msgstr ""
 
-#: aleksis/core/models.py:179
+#: aleksis/core/models.py:183
 msgid "Additional name(s)"
 msgstr ""
 
-#: aleksis/core/models.py:183 aleksis/core/models.py:429
+#: aleksis/core/models.py:187 aleksis/core/models.py:431
 msgid "Short name"
 msgstr ""
 
-#: aleksis/core/models.py:186
+#: aleksis/core/models.py:190
 msgid "Street"
 msgstr ""
 
-#: aleksis/core/models.py:187
+#: aleksis/core/models.py:191
 msgid "Street number"
 msgstr ""
 
-#: aleksis/core/models.py:188
+#: aleksis/core/models.py:192
 msgid "Postal code"
 msgstr ""
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:193
 msgid "Place"
 msgstr ""
 
-#: aleksis/core/models.py:191
+#: aleksis/core/models.py:195
 msgid "Home phone"
 msgstr ""
 
-#: aleksis/core/models.py:192
+#: aleksis/core/models.py:196
 msgid "Mobile phone"
 msgstr ""
 
-#: aleksis/core/models.py:196
+#: aleksis/core/models.py:200
 msgid "Date of birth"
 msgstr ""
 
-#: aleksis/core/models.py:198
+#: aleksis/core/models.py:201
 msgid "Place of birth"
 msgstr ""
 
-#: aleksis/core/models.py:200
+#: aleksis/core/models.py:202
 msgid "Sex"
 msgstr ""
 
-#: aleksis/core/models.py:202
+#: aleksis/core/models.py:204
 msgid "Photo"
 msgstr ""
 
-#: aleksis/core/models.py:206 aleksis/core/templates/core/person/full.html:138
+#: aleksis/core/models.py:208 aleksis/core/templates/core/person/full.html:145
 msgid "Guardians / Parents"
 msgstr ""
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:215
 msgid "Primary group"
 msgstr ""
 
-#: aleksis/core/models.py:216 aleksis/core/models.py:583
-#: aleksis/core/models.py:607 aleksis/core/models.py:692
-#: aleksis/core/models.py:972 aleksis/core/templates/core/person/full.html:121
+#: aleksis/core/models.py:218 aleksis/core/models.py:585
+#: aleksis/core/models.py:609 aleksis/core/models.py:694
+#: aleksis/core/models.py:974 aleksis/core/templates/core/person/full.html:128
 msgid "Description"
 msgstr ""
 
-#: aleksis/core/models.py:384
+#: aleksis/core/models.py:386
 msgid "Title of field"
 msgstr ""
 
-#: aleksis/core/models.py:386
+#: aleksis/core/models.py:388
 msgid "Type of field"
 msgstr ""
 
-#: aleksis/core/models.py:393
+#: aleksis/core/models.py:395
 msgid "Addtitional field for groups"
 msgstr ""
 
-#: aleksis/core/models.py:394
+#: aleksis/core/models.py:396
 msgid "Addtitional fields for groups"
 msgstr ""
 
-#: aleksis/core/models.py:414
+#: aleksis/core/models.py:416
 msgid "Can assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/models.py:415
+#: aleksis/core/models.py:417
 msgid "Can view statistics about group."
 msgstr ""
 
-#: aleksis/core/models.py:427
+#: aleksis/core/models.py:429
 msgid "Long name"
 msgstr ""
 
-#: aleksis/core/models.py:437 aleksis/core/templates/core/group/full.html:85
+#: aleksis/core/models.py:439 aleksis/core/templates/core/group/full.html:85
 msgid "Members"
 msgstr ""
 
-#: aleksis/core/models.py:440 aleksis/core/templates/core/group/full.html:82
+#: aleksis/core/models.py:442 aleksis/core/templates/core/group/full.html:82
 msgid "Owners"
 msgstr ""
 
-#: aleksis/core/models.py:447 aleksis/core/templates/core/group/full.html:55
+#: aleksis/core/models.py:449 aleksis/core/templates/core/group/full.html:55
 msgid "Parent groups"
 msgstr ""
 
-#: aleksis/core/models.py:455
+#: aleksis/core/models.py:457
 msgid "Type of group"
 msgstr ""
 
-#: aleksis/core/models.py:582 aleksis/core/models.py:606
-#: aleksis/core/models.py:691
+#: aleksis/core/models.py:584 aleksis/core/models.py:608
+#: aleksis/core/models.py:693
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr ""
 
-#: aleksis/core/models.py:585
+#: aleksis/core/models.py:587
 msgid "Application"
 msgstr ""
 
-#: aleksis/core/models.py:591
+#: aleksis/core/models.py:593
 msgid "Activity"
 msgstr ""
 
-#: aleksis/core/models.py:592
+#: aleksis/core/models.py:594
 msgid "Activities"
 msgstr ""
 
-#: aleksis/core/models.py:598
+#: aleksis/core/models.py:600
 msgid "Sender"
 msgstr ""
 
-#: aleksis/core/models.py:603
+#: aleksis/core/models.py:605
 msgid "Recipient"
 msgstr ""
 
-#: aleksis/core/models.py:608 aleksis/core/models.py:949
+#: aleksis/core/models.py:610 aleksis/core/models.py:951
 msgid "Link"
 msgstr ""
 
-#: aleksis/core/models.py:610
+#: aleksis/core/models.py:612
 msgid "Read"
 msgstr ""
 
-#: aleksis/core/models.py:611
+#: aleksis/core/models.py:613
 msgid "Sent"
 msgstr ""
 
-#: aleksis/core/models.py:624
+#: aleksis/core/models.py:626
 msgid "Notification"
 msgstr ""
 
-#: aleksis/core/models.py:693
+#: aleksis/core/models.py:695
 msgid "Link to detailed view"
 msgstr ""
 
-#: aleksis/core/models.py:696
+#: aleksis/core/models.py:698
 msgid "Date and time from when to show"
 msgstr ""
 
-#: aleksis/core/models.py:699
+#: aleksis/core/models.py:701
 msgid "Date and time until when to show"
 msgstr ""
 
-#: aleksis/core/models.py:724
+#: aleksis/core/models.py:726
 msgid "Announcement"
 msgstr ""
 
-#: aleksis/core/models.py:762
+#: aleksis/core/models.py:764
 msgid "Announcement recipient"
 msgstr ""
 
-#: aleksis/core/models.py:763
+#: aleksis/core/models.py:765
 msgid "Announcement recipients"
 msgstr ""
 
-#: aleksis/core/models.py:818
+#: aleksis/core/models.py:820
 msgid "Widget Title"
 msgstr ""
 
-#: aleksis/core/models.py:819
+#: aleksis/core/models.py:821
 msgid "Activate Widget"
 msgstr ""
 
-#: aleksis/core/models.py:820
+#: aleksis/core/models.py:822
 msgid "Widget is broken"
 msgstr ""
 
-#: aleksis/core/models.py:823
+#: aleksis/core/models.py:825
 msgid "Size on mobile devices"
 msgstr ""
 
-#: aleksis/core/models.py:824
+#: aleksis/core/models.py:826
 msgid "<= 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:829
+#: aleksis/core/models.py:831
 msgid "Size on tablet devices"
 msgstr ""
 
-#: aleksis/core/models.py:830
+#: aleksis/core/models.py:832
 msgid "> 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:835
+#: aleksis/core/models.py:837
 msgid "Size on desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:836
+#: aleksis/core/models.py:838
 msgid "> 992 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:841
+#: aleksis/core/models.py:843
 msgid "Size on large desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:842
+#: aleksis/core/models.py:844
 msgid "> 1200 px>, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:873
+#: aleksis/core/models.py:875
 msgid "Can edit default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:874
+#: aleksis/core/models.py:876
 msgid "Dashboard Widget"
 msgstr ""
 
-#: aleksis/core/models.py:875
+#: aleksis/core/models.py:877
 msgid "Dashboard Widgets"
 msgstr ""
 
-#: aleksis/core/models.py:881
+#: aleksis/core/models.py:883
 msgid "URL"
 msgstr ""
 
-#: aleksis/core/models.py:882
+#: aleksis/core/models.py:884
 msgid "Icon URL"
 msgstr ""
 
-#: aleksis/core/models.py:888
+#: aleksis/core/models.py:890
 msgid "External link widget"
 msgstr ""
 
-#: aleksis/core/models.py:889
+#: aleksis/core/models.py:891
 msgid "External link widgets"
 msgstr ""
 
-#: aleksis/core/models.py:894
+#: aleksis/core/models.py:896
 msgid "Dashboard widget"
 msgstr ""
 
-#: aleksis/core/models.py:899
+#: aleksis/core/models.py:901
 msgid "Order"
 msgstr ""
 
-#: aleksis/core/models.py:900
+#: aleksis/core/models.py:902
 msgid "Part of the default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:915
+#: aleksis/core/models.py:917
 msgid "Dashboard widget order"
 msgstr ""
 
-#: aleksis/core/models.py:916
+#: aleksis/core/models.py:918
 msgid "Dashboard widget orders"
 msgstr ""
 
-#: aleksis/core/models.py:922
+#: aleksis/core/models.py:924
 msgid "Menu ID"
 msgstr ""
 
-#: aleksis/core/models.py:935
+#: aleksis/core/models.py:937
 msgid "Custom menu"
 msgstr ""
 
-#: aleksis/core/models.py:936
+#: aleksis/core/models.py:938
 msgid "Custom menus"
 msgstr ""
 
-#: aleksis/core/models.py:946
+#: aleksis/core/models.py:948
 msgid "Menu"
 msgstr ""
 
-#: aleksis/core/models.py:950
+#: aleksis/core/models.py:952
 msgid "Icon"
 msgstr ""
 
-#: aleksis/core/models.py:956
+#: aleksis/core/models.py:958
 msgid "Custom menu item"
 msgstr ""
 
-#: aleksis/core/models.py:957
+#: aleksis/core/models.py:959
 msgid "Custom menu items"
 msgstr ""
 
-#: aleksis/core/models.py:971
+#: aleksis/core/models.py:973
 msgid "Title of type"
 msgstr ""
 
-#: aleksis/core/models.py:978 aleksis/core/templates/core/group/full.html:47
+#: aleksis/core/models.py:980 aleksis/core/templates/core/group/full.html:47
 msgid "Group type"
 msgstr ""
 
-#: aleksis/core/models.py:992
+#: aleksis/core/models.py:994
 msgid "Can view system status"
 msgstr ""
 
-#: aleksis/core/models.py:993
+#: aleksis/core/models.py:995
 msgid "Can manage data"
 msgstr ""
 
-#: aleksis/core/models.py:994
+#: aleksis/core/models.py:996
 msgid "Can impersonate"
 msgstr ""
 
-#: aleksis/core/models.py:995
+#: aleksis/core/models.py:997
 msgid "Can use search"
 msgstr ""
 
-#: aleksis/core/models.py:996
+#: aleksis/core/models.py:998
 msgid "Can change site preferences"
 msgstr ""
 
-#: aleksis/core/models.py:997
+#: aleksis/core/models.py:999
 msgid "Can change person preferences"
 msgstr ""
 
-#: aleksis/core/models.py:998
+#: aleksis/core/models.py:1000
 msgid "Can change group preferences"
 msgstr ""
 
-#: aleksis/core/models.py:999
+#: aleksis/core/models.py:1001
 msgid "Can test PDF generation"
 msgstr ""
 
-#: aleksis/core/models.py:1035
+#: aleksis/core/models.py:1037
 msgid "Related data check task"
 msgstr ""
 
-#: aleksis/core/models.py:1043
+#: aleksis/core/models.py:1045
 msgid "Issue solved"
 msgstr ""
 
-#: aleksis/core/models.py:1044
+#: aleksis/core/models.py:1046
 msgid "Notification sent"
 msgstr ""
 
-#: aleksis/core/models.py:1057
+#: aleksis/core/models.py:1059
 msgid "Data check result"
 msgstr ""
 
-#: aleksis/core/models.py:1058
+#: aleksis/core/models.py:1060
 msgid "Data check results"
 msgstr ""
 
-#: aleksis/core/models.py:1060
+#: aleksis/core/models.py:1062
 msgid "Can run data checks"
 msgstr ""
 
-#: aleksis/core/models.py:1061
+#: aleksis/core/models.py:1063
 msgid "Can solve data check problems"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1070
+msgid "E-Mail address"
+msgstr ""
+
+#: aleksis/core/models.py:1094
 msgid "Owner"
 msgstr ""
 
-#: aleksis/core/models.py:1080
+#: aleksis/core/models.py:1098
 msgid "File expires at"
 msgstr ""
 
-#: aleksis/core/models.py:1082
+#: aleksis/core/models.py:1100
 msgid "Generated HTML file"
 msgstr ""
 
-#: aleksis/core/models.py:1084
+#: aleksis/core/models.py:1102
 msgid "Generated PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1091
+#: aleksis/core/models.py:1109
 msgid "PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1092
+#: aleksis/core/models.py:1110
 msgid "PDF files"
 msgstr ""
 
-#: aleksis/core/models.py:1097
+#: aleksis/core/models.py:1115
 msgid "Task result"
 msgstr ""
 
-#: aleksis/core/models.py:1100
+#: aleksis/core/models.py:1118
 msgid "Task user"
 msgstr ""
 
-#: aleksis/core/models.py:1112
+#: aleksis/core/models.py:1130
 msgid "Task user assignment"
 msgstr ""
 
-#: aleksis/core/models.py:1113
+#: aleksis/core/models.py:1131
 msgid "Task user assignments"
 msgstr ""
 
-#: aleksis/core/models.py:1127
+#: aleksis/core/models.py:1147
+msgid "Additional attributes"
+msgstr ""
+
+#: aleksis/core/models.py:1185
 msgid "Allowed scopes that clients can request"
 msgstr ""
 
-#: aleksis/core/preferences.py:23
+#: aleksis/core/preferences.py:24
 msgid "General"
 msgstr ""
 
-#: aleksis/core/preferences.py:24
+#: aleksis/core/preferences.py:25
 msgid "School"
 msgstr ""
 
-#: aleksis/core/preferences.py:25
+#: aleksis/core/preferences.py:26
 msgid "Theme"
 msgstr ""
 
-#: aleksis/core/preferences.py:26
+#: aleksis/core/preferences.py:27
 msgid "Mail"
 msgstr ""
 
-#: aleksis/core/preferences.py:28
+#: aleksis/core/preferences.py:29
 msgid "Footer"
 msgstr ""
 
-#: aleksis/core/preferences.py:29
+#: aleksis/core/preferences.py:30
 msgid "Accounts"
 msgstr ""
 
-#: aleksis/core/preferences.py:30
+#: aleksis/core/preferences.py:31
 msgid "Authentication"
 msgstr ""
 
-#: aleksis/core/preferences.py:31
+#: aleksis/core/preferences.py:32
 msgid "Internationalisation"
 msgstr ""
 
-#: aleksis/core/preferences.py:42
+#: aleksis/core/preferences.py:43
 msgid "Site title"
 msgstr ""
 
-#: aleksis/core/preferences.py:53
+#: aleksis/core/preferences.py:54
 msgid "Site description"
 msgstr ""
 
-#: aleksis/core/preferences.py:64
+#: aleksis/core/preferences.py:65
 msgid "Primary colour"
 msgstr ""
 
-#: aleksis/core/preferences.py:76
+#: aleksis/core/preferences.py:77
 msgid "Secondary colour"
 msgstr ""
 
-#: aleksis/core/preferences.py:87
+#: aleksis/core/preferences.py:88
 msgid "Logo"
 msgstr ""
 
-#: aleksis/core/preferences.py:97
+#: aleksis/core/preferences.py:98
 msgid "Favicon"
 msgstr ""
 
-#: aleksis/core/preferences.py:107
+#: aleksis/core/preferences.py:108
 msgid "PWA-Icon"
 msgstr ""
 
-#: aleksis/core/preferences.py:118
+#: aleksis/core/preferences.py:119
 msgid "Mail out name"
 msgstr ""
 
-#: aleksis/core/preferences.py:129
+#: aleksis/core/preferences.py:130
 msgid "Mail out address"
 msgstr ""
 
-#: aleksis/core/preferences.py:141
+#: aleksis/core/preferences.py:142
 msgid "Link to privacy policy"
 msgstr ""
 
-#: aleksis/core/preferences.py:153
+#: aleksis/core/preferences.py:154
 msgid "Link to imprint"
 msgstr ""
 
-#: aleksis/core/preferences.py:165
+#: aleksis/core/preferences.py:166
 msgid "Name format for addressing"
 msgstr ""
 
-#: aleksis/core/preferences.py:181
+#: aleksis/core/preferences.py:182
 msgid "Channels to use for notifications"
 msgstr ""
 
-#: aleksis/core/preferences.py:193
+#: aleksis/core/preferences.py:194
 msgid "Regular expression to match primary group, e.g. '^Class .*'"
 msgstr ""
 
-#: aleksis/core/preferences.py:204
+#: aleksis/core/preferences.py:205
 msgid "Field on person to match primary group against"
 msgstr ""
 
-#: aleksis/core/preferences.py:216
+#: aleksis/core/preferences.py:217
 msgid "Automatically create new persons for new users"
 msgstr ""
 
-#: aleksis/core/preferences.py:225
+#: aleksis/core/preferences.py:226
 msgid "Automatically link existing persons to new users by their e-mail address"
 msgstr ""
 
-#: aleksis/core/preferences.py:236
+#: aleksis/core/preferences.py:237
 msgid "Display name of the school"
 msgstr ""
 
-#: aleksis/core/preferences.py:247
+#: aleksis/core/preferences.py:248
 msgid "Official name of the school, e.g. as given by supervisory authority"
 msgstr ""
 
-#: aleksis/core/preferences.py:255
+#: aleksis/core/preferences.py:256
 msgid "Allow users to change their passwords"
 msgstr ""
 
-#: aleksis/core/preferences.py:263
+#: aleksis/core/preferences.py:264
 msgid "Enable signup"
 msgstr ""
 
-#: aleksis/core/preferences.py:274
+#: aleksis/core/preferences.py:272
+msgid "Enable invitations"
+msgstr ""
+
+#: aleksis/core/preferences.py:280
+msgid "Length of invite code. (Default 3: abcde-acbde-abcde)"
+msgstr ""
+
+#: aleksis/core/preferences.py:288
+msgid "Size of packets. (Default 5: abcde)"
+msgstr ""
+
+#: aleksis/core/preferences.py:298
 msgid "Allowed Grant Flows for OAuth applications"
 msgstr ""
 
-#: aleksis/core/preferences.py:287
+#: aleksis/core/preferences.py:311
 msgid "Available languages"
 msgstr ""
 
-#: aleksis/core/preferences.py:299
+#: aleksis/core/preferences.py:323
 msgid "Send emails if data checks detect problems"
 msgstr ""
 
-#: aleksis/core/preferences.py:310
+#: aleksis/core/preferences.py:334
 msgid "Email recipients for data checks problem emails"
 msgstr ""
 
-#: aleksis/core/preferences.py:321
+#: aleksis/core/preferences.py:345
 msgid "Email recipient groups for data checks problem emails"
 msgstr ""
 
-#: aleksis/core/preferences.py:330
+#: aleksis/core/preferences.py:354
 msgid "Show dashboard to users without login"
 msgstr ""
 
-#: aleksis/core/preferences.py:339
+#: aleksis/core/preferences.py:363
 msgid "Allow users to edit their dashboard"
 msgstr ""
 
-#: aleksis/core/preferences.py:350
+#: aleksis/core/preferences.py:374
 msgid "Fields on person model which are editable by themselves."
 msgstr ""
 
-#: aleksis/core/preferences.py:364
+#: aleksis/core/preferences.py:388
 msgid "Editable fields on person model which should trigger a notification on change"
 msgstr ""
 
-#: aleksis/core/preferences.py:377
+#: aleksis/core/preferences.py:401
 msgid "Contact for notification if a person changes their data"
 msgstr ""
 
-#: aleksis/core/preferences.py:387
+#: aleksis/core/preferences.py:411
 msgid "PDF file expiration duration"
 msgstr ""
 
-#: aleksis/core/preferences.py:388
+#: aleksis/core/preferences.py:412
 msgid "in minutes"
 msgstr ""
 
-#: aleksis/core/preferences.py:398
+#: aleksis/core/preferences.py:422
 msgid "Automatically update the dashboard and its widgets"
 msgstr ""
 
-#: aleksis/core/preferences.py:408
+#: aleksis/core/preferences.py:432
 msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr ""
 
-#: aleksis/core/settings.py:474
+#: aleksis/core/settings.py:507
 msgid "English"
 msgstr ""
 
-#: aleksis/core/settings.py:475
+#: aleksis/core/settings.py:508
 msgid "German"
 msgstr ""
 
-#: aleksis/core/tables.py:19
+#: aleksis/core/tables.py:24
 #: aleksis/core/templates/core/announcement/list.html:36
 #: aleksis/core/templates/core/group/full.html:24
-#: aleksis/core/templates/core/person/full.html:23
+#: aleksis/core/templates/core/person/full.html:24
 #: aleksis/core/templates/oauth2_provider/application/detail.html:17
 msgid "Edit"
 msgstr ""
 
-#: aleksis/core/tables.py:21 aleksis/core/tables.py:89
-#: aleksis/core/tables.py:105
+#: aleksis/core/tables.py:26 aleksis/core/tables.py:94
+#: aleksis/core/tables.py:137
 #: aleksis/core/templates/core/announcement/list.html:22
 msgid "Actions"
 msgstr ""
 
-#: aleksis/core/tables.py:56 aleksis/core/tables.py:57
-#: aleksis/core/tables.py:71 aleksis/core/tables.py:87
-#: aleksis/core/tables.py:103
+#: aleksis/core/tables.py:61 aleksis/core/tables.py:62
+#: aleksis/core/tables.py:76 aleksis/core/tables.py:92
+#: aleksis/core/tables.py:135
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/group/full.html:31
 #: aleksis/core/templates/core/pages/delete.html:22
-#: aleksis/core/templates/core/person/full.html:30
+#: aleksis/core/templates/core/person/full.html:31
 #: aleksis/core/templates/oauth2_provider/application/detail.html:21
 msgid "Delete"
 msgstr ""
@@ -1207,11 +1226,11 @@ msgstr ""
 msgid "Account inactive"
 msgstr ""
 
-#: aleksis/core/templates/account/account_inactive.html:13
+#: aleksis/core/templates/account/account_inactive.html:14
 msgid "Account inactive."
 msgstr ""
 
-#: aleksis/core/templates/account/account_inactive.html:15
+#: aleksis/core/templates/account/account_inactive.html:17
 msgid ""
 "\n"
 "            This account is currently inactive. If you think this is an\n"
@@ -1363,11 +1382,11 @@ msgstr ""
 msgid "Signup closed"
 msgstr ""
 
-#: aleksis/core/templates/account/signup_closed.html:13
+#: aleksis/core/templates/account/signup_closed.html:14
 msgid "Signup closed."
 msgstr ""
 
-#: aleksis/core/templates/account/signup_closed.html:15
+#: aleksis/core/templates/account/signup_closed.html:17
 msgid ""
 "\n"
 "            This sign up is currently closed. If you think this is an\n"
@@ -1405,11 +1424,6 @@ msgid ""
 "          "
 msgstr ""
 
-#: aleksis/core/templates/account/verification_sent.html:30
-#, python-format
-msgid "<strong>Note:</strong> you can still <a href=\"%(email_url)s\">change your e-mail address</a>"
-msgstr ""
-
 #: aleksis/core/templates/core/additional_field/edit.html:6
 #: aleksis/core/templates/core/additional_field/edit.html:7
 msgid "Edit additional field"
@@ -1458,11 +1472,11 @@ msgid "Logged in as"
 msgstr ""
 
 #: aleksis/core/templates/core/base.html:175
-msgid "About AlekSIS — The Free School Information System"
+msgid "About AlekSIS® — The Free School Information System"
 msgstr ""
 
 #: aleksis/core/templates/core/base.html:183
-msgid "Impress"
+msgid "Imprint"
 msgstr ""
 
 #: aleksis/core/templates/core/base.html:191
@@ -1470,7 +1484,7 @@ msgid "Privacy Policy"
 msgstr ""
 
 #: aleksis/core/templates/core/base_print.html:72
-msgid "Powered by AlekSIS"
+msgid "Powered by AlekSIS®"
 msgstr ""
 
 #: aleksis/core/templates/core/dashboard_widget/create.html:8
@@ -1685,7 +1699,7 @@ msgid "Edit group"
 msgstr ""
 
 #: aleksis/core/templates/core/group/full.html:38
-#: aleksis/core/templates/core/person/full.html:37
+#: aleksis/core/templates/core/person/full.html:38
 msgid "Change preferences"
 msgstr ""
 
@@ -1774,36 +1788,46 @@ msgid "No notifications available yet."
 msgstr ""
 
 #: aleksis/core/templates/core/pages/about.html:6
-#: aleksis/core/templates/core/pages/about.html:15
-msgid "About AlekSIS"
+msgid "About AlekSIS®"
 msgstr ""
 
 #: aleksis/core/templates/core/pages/about.html:7
-msgid "AlekSIS – The Free School Information System"
+msgid "AlekSIS® – The Free School Information System"
+msgstr ""
+
+#: aleksis/core/templates/core/pages/about.html:15
+msgid "About AlekSIS"
 msgstr ""
 
 #: aleksis/core/templates/core/pages/about.html:17
 msgid ""
 "\n"
-"              This platform is powered by AlekSIS, a web-based school information system (SIS) which can be used\n"
+"              This platform is powered by AlekSIS®, a web-based school information system (SIS) which can be used\n"
 "              to manage and/or publish organisational artifacts of educational institutions. AlekSIS is free software and\n"
 "              can be used by anyone.\n"
 "            "
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:25
+#: aleksis/core/templates/core/pages/about.html:24
+msgid ""
+"\n"
+"              AlekSIS® is a registered trademark of the AlekSIS open source project, represented by Teckids e.V.\n"
+"            "
+msgstr ""
+
+#: aleksis/core/templates/core/pages/about.html:30
 msgid "Website of AlekSIS"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:26
+#: aleksis/core/templates/core/pages/about.html:31
 msgid "Source code"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:35
+#: aleksis/core/templates/core/pages/about.html:40
 msgid "Licence information"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:37
+#: aleksis/core/templates/core/pages/about.html:42
 msgid ""
 "\n"
 "              The core and the official apps of AlekSIS are licenced under the EUPL, version 1.2 or later. For licence\n"
@@ -1812,23 +1836,23 @@ msgid ""
 "            "
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:45
+#: aleksis/core/templates/core/pages/about.html:50
 msgid "Free/Open Source Licence"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:46
+#: aleksis/core/templates/core/pages/about.html:51
 msgid "Other Licence"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:50
+#: aleksis/core/templates/core/pages/about.html:55
 msgid "Full licence text"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:51
+#: aleksis/core/templates/core/pages/about.html:56
 msgid "More information about the EUPL"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:90
+#: aleksis/core/templates/core/pages/about.html:95
 #, python-format
 msgid ""
 "\n"
@@ -2069,17 +2093,19 @@ msgstr ""
 msgid "Edit person"
 msgstr ""
 
-#: aleksis/core/templates/core/person/full.html:44
-#: aleksis/core/templates/impersonate/list_users.html:7
-#: aleksis/core/templates/impersonate/list_users.html:8
+#: aleksis/core/templates/core/person/full.html:45
 msgid "Impersonate"
 msgstr ""
 
-#: aleksis/core/templates/core/person/full.html:50
+#: aleksis/core/templates/core/person/full.html:51
+msgid "Invite user"
+msgstr ""
+
+#: aleksis/core/templates/core/person/full.html:57
 msgid "Contact details"
 msgstr ""
 
-#: aleksis/core/templates/core/person/full.html:131
+#: aleksis/core/templates/core/person/full.html:138
 msgid "Children"
 msgstr ""
 
@@ -2119,6 +2145,49 @@ msgstr ""
 msgid "Save preferences"
 msgstr ""
 
+#: aleksis/core/templates/invitations/enter.html:21
+msgid "Accept your invitation"
+msgstr ""
+
+#: aleksis/core/templates/invitations/enter.html:25
+msgid ""
+"\n"
+"                Please enter your invitation code to register\n"
+"                your new user account:\n"
+"              "
+msgstr ""
+
+#: aleksis/core/templates/invitations/enter.html:37
+msgid "Accept invite"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:9
+#: aleksis/core/templates/invitations/forms/_invite.html:10
+#: aleksis/core/templates/invitations/forms/_invite.html:21
+msgid "Invite"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:17
+msgid "Invite by email address"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:26
+msgid "Generate invitation code"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:29
+msgid "Generate code"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:33
+msgid "Invitations"
+msgstr ""
+
+#: aleksis/core/templates/invitations/messages/invite_accepted.txt:3
+#, python-format
+msgid "The invitation for %(email)s has been accepted."
+msgstr ""
+
 #: aleksis/core/templates/oauth2_provider/application/create.html:5
 #: aleksis/core/templates/oauth2_provider/application/create.html:6
 msgid "Register OAuth2 Application"
@@ -2775,150 +2844,163 @@ msgstr ""
 msgid "SMS"
 msgstr ""
 
-#: aleksis/core/util/pdf.py:105
+#: aleksis/core/util/pdf.py:113
 msgid "Progress: Generate PDF file"
 msgstr ""
 
-#: aleksis/core/util/pdf.py:106
+#: aleksis/core/util/pdf.py:114
 msgid "Generating PDF file …"
 msgstr ""
 
-#: aleksis/core/util/pdf.py:107
+#: aleksis/core/util/pdf.py:115
 msgid "The PDF file has been generated successfully."
 msgstr ""
 
-#: aleksis/core/util/pdf.py:108
+#: aleksis/core/util/pdf.py:116
 msgid "There was a problem while generating the PDF file."
 msgstr ""
 
-#: aleksis/core/util/pdf.py:111
+#: aleksis/core/util/pdf.py:119
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/core/views.py:270
+#: aleksis/core/views.py:280
 msgid "The school term has been created."
 msgstr ""
 
-#: aleksis/core/views.py:282
+#: aleksis/core/views.py:292
 msgid "The school term has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:406
+#: aleksis/core/views.py:416
 msgid "The child groups were successfully saved."
 msgstr ""
 
-#: aleksis/core/views.py:425 aleksis/core/views.py:435
+#: aleksis/core/views.py:435 aleksis/core/views.py:445
 msgid "The person has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:485
+#: aleksis/core/views.py:495
 msgid "The group has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:582
+#: aleksis/core/views.py:592
 msgid "The announcement has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:598
+#: aleksis/core/views.py:608
 msgid "The announcement has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:682
+#: aleksis/core/views.py:695
 msgid "The preferences have been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:706
+#: aleksis/core/views.py:719
 msgid "The person has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:720
+#: aleksis/core/views.py:733
 msgid "The group has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:752
+#: aleksis/core/views.py:765
 msgid "The additional_field has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:786
+#: aleksis/core/views.py:799
 msgid "The additional field has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:811
+#: aleksis/core/views.py:824
 msgid "The group type has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:841
+#: aleksis/core/views.py:854
 msgid "The group type has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:874
+#: aleksis/core/views.py:887
 msgid "Progress: Run data checks"
 msgstr ""
 
-#: aleksis/core/views.py:875
+#: aleksis/core/views.py:888
 msgid "Run data checks …"
 msgstr ""
 
-#: aleksis/core/views.py:876
+#: aleksis/core/views.py:889
 msgid "The data checks were run successfully."
 msgstr ""
 
-#: aleksis/core/views.py:877
+#: aleksis/core/views.py:890
 msgid "There was a problem while running data checks."
 msgstr ""
 
-#: aleksis/core/views.py:893
+#: aleksis/core/views.py:906
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr ""
 
-#: aleksis/core/views.py:935
+#: aleksis/core/views.py:948
 msgid "The dashboard widget has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:965
+#: aleksis/core/views.py:978
 msgid "The dashboard widget has been created."
 msgstr ""
 
-#: aleksis/core/views.py:975
+#: aleksis/core/views.py:988
 msgid "The dashboard widget has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1042
+#: aleksis/core/views.py:1055
 msgid "Your dashboard configuration has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1044
+#: aleksis/core/views.py:1057
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1139
+#: aleksis/core/views.py:1127
+#, python-brace-format
+msgid "The invitation was successfully created. The invitation code is {code}"
+msgstr ""
+
+#: aleksis/core/views.py:1218
 msgid "We have successfully assigned the permissions."
 msgstr ""
 
-#: aleksis/core/views.py:1149
+#: aleksis/core/views.py:1228
 msgid "The global user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1159
+#: aleksis/core/views.py:1238
 msgid "The global group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1169
+#: aleksis/core/views.py:1248
 msgid "The object user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1179
+#: aleksis/core/views.py:1258
 msgid "The object group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1298
+#: aleksis/core/views.py:1377
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr ""
 
-#: aleksis/core/views.py:1305
+#: aleksis/core/views.py:1384
 msgid "The third-party account has been successfully disconnected."
 msgstr ""
 
+#: aleksis/core/views.py:1441
+msgid "Person was invited successfully."
+msgstr ""
+
+#: aleksis/core/views.py:1443
+msgid "Person was already invited."
+msgstr ""
+
 #~ msgid "Norwegian (bokmål)"
 #~ msgstr "Norsk (bokmål)"
diff --git a/aleksis/core/locale/nb_NO/LC_MESSAGES/djangojs.po b/aleksis/core/locale/nb_NO/LC_MESSAGES/djangojs.po
index 2458849847f712fe1cf63743640716c5b3ef561a..d67bdb0055bf1ee96915909ce9e21023085c466b 100644
--- a/aleksis/core/locale/nb_NO/LC_MESSAGES/djangojs.po
+++ b/aleksis/core/locale/nb_NO/LC_MESSAGES/djangojs.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-11-29 09:59+0100\n"
+"POT-Creation-Date: 2021-12-28 12:14+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -29,6 +29,6 @@ msgstr ""
 msgid "OK"
 msgstr ""
 
-#: aleksis/core/static/js/main.js:127
+#: aleksis/core/static/js/main.js:128
 msgid "This page may contain outdated information since there is no internet connection."
 msgstr ""
diff --git a/aleksis/core/locale/tr_TR/LC_MESSAGES/django.po b/aleksis/core/locale/tr_TR/LC_MESSAGES/django.po
index 018414f7c82920ea684c99d5574e52bc2a923d5a..1738a0918a91e64c2eb5421ef10b70dac77a6b90 100644
--- a/aleksis/core/locale/tr_TR/LC_MESSAGES/django.po
+++ b/aleksis/core/locale/tr_TR/LC_MESSAGES/django.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: AlekSIS (School Information System) 0.1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-11-29 09:59+0100\n"
+"POT-Creation-Date: 2021-12-28 12:13+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,26 +17,33 @@ msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: aleksis/core/apps.py:151
+#: aleksis/core/apps.py:152
 msgid "OpenID Connect scope"
 msgstr ""
 
-#: aleksis/core/apps.py:152
+#: aleksis/core/apps.py:153
 msgid "Given name, family name, link to profile and picture if existing."
 msgstr ""
 
-#: aleksis/core/apps.py:153
+#: aleksis/core/apps.py:154
 msgid "Full home postal address"
 msgstr ""
 
-#: aleksis/core/apps.py:154
+#: aleksis/core/apps.py:155
 msgid "Email address"
 msgstr ""
 
-#: aleksis/core/apps.py:155
+#: aleksis/core/apps.py:156
 msgid "Home and mobile phone"
 msgstr ""
 
+#: aleksis/core/apps.py:157 aleksis/core/forms.py:223 aleksis/core/menus.py:265
+#: aleksis/core/models.py:414 aleksis/core/templates/core/group/list.html:8
+#: aleksis/core/templates/core/group/list.html:9
+#: aleksis/core/templates/core/person/full.html:152
+msgid "Groups"
+msgstr ""
+
 #: aleksis/core/data_checks.py:55
 msgid "Ignore problem"
 msgstr ""
@@ -83,156 +90,148 @@ msgstr ""
 msgid "Content type"
 msgstr ""
 
-#: aleksis/core/filters.py:112 aleksis/core/models.py:579
+#: aleksis/core/filters.py:112 aleksis/core/models.py:581
 msgid "User"
 msgstr ""
 
-#: aleksis/core/filters.py:134 aleksis/core/models.py:411
+#: aleksis/core/filters.py:134 aleksis/core/models.py:413
 msgid "Group"
 msgstr ""
 
-#: aleksis/core/forms.py:46 aleksis/core/forms.py:514
+#: aleksis/core/forms.py:48 aleksis/core/forms.py:559
 msgid "Base data"
 msgstr ""
 
-#: aleksis/core/forms.py:52
+#: aleksis/core/forms.py:54
 msgid "Address"
 msgstr ""
 
-#: aleksis/core/forms.py:53
+#: aleksis/core/forms.py:55 aleksis/core/forms.py:568
 msgid "Contact data"
 msgstr ""
 
-#: aleksis/core/forms.py:55
+#: aleksis/core/forms.py:57
 msgid "Advanced personal data"
 msgstr ""
 
-#: aleksis/core/forms.py:103
+#: aleksis/core/forms.py:105
 msgid "New user"
 msgstr ""
 
-#: aleksis/core/forms.py:103
+#: aleksis/core/forms.py:105
 msgid "Create a new account"
 msgstr ""
 
-#: aleksis/core/forms.py:134
+#: aleksis/core/forms.py:136
 msgid "You cannot set a new username when also selecting an existing user."
 msgstr ""
 
-#: aleksis/core/forms.py:138
+#: aleksis/core/forms.py:140
 msgid "This username is already in use."
 msgstr ""
 
-#: aleksis/core/forms.py:155 aleksis/core/models.py:125
+#: aleksis/core/forms.py:157 aleksis/core/models.py:129
 msgid "School term"
 msgstr ""
 
-#: aleksis/core/forms.py:156
+#: aleksis/core/forms.py:158
 msgid "Common data"
 msgstr ""
 
-#: aleksis/core/forms.py:157 aleksis/core/forms.py:208
-#: aleksis/core/menus.py:256 aleksis/core/models.py:148
+#: aleksis/core/forms.py:159 aleksis/core/forms.py:210
+#: aleksis/core/menus.py:254 aleksis/core/models.py:152
 #: aleksis/core/templates/core/person/list.html:8
 #: aleksis/core/templates/core/person/list.html:9
 msgid "Persons"
 msgstr ""
 
-#: aleksis/core/forms.py:158
+#: aleksis/core/forms.py:160 aleksis/core/forms.py:570
 msgid "Additional data"
 msgstr ""
 
-#: aleksis/core/forms.py:200 aleksis/core/forms.py:203
-#: aleksis/core/models.py:68
+#: aleksis/core/forms.py:202 aleksis/core/forms.py:205
+#: aleksis/core/models.py:72
 msgid "Date"
 msgstr ""
 
-#: aleksis/core/forms.py:201 aleksis/core/forms.py:204
-#: aleksis/core/models.py:76
+#: aleksis/core/forms.py:203 aleksis/core/forms.py:206
+#: aleksis/core/models.py:80
 msgid "Time"
 msgstr ""
 
-#: aleksis/core/forms.py:221 aleksis/core/menus.py:267
-#: aleksis/core/models.py:412 aleksis/core/templates/core/group/list.html:8
-#: aleksis/core/templates/core/group/list.html:9
-#: aleksis/core/templates/core/person/full.html:145
-msgid "Groups"
-msgstr ""
-
-#: aleksis/core/forms.py:234
+#: aleksis/core/forms.py:236
 msgid "From when until when should the announcement be displayed?"
 msgstr ""
 
-#: aleksis/core/forms.py:237
+#: aleksis/core/forms.py:239
 msgid "Who should see the announcement?"
 msgstr ""
 
-#: aleksis/core/forms.py:238
+#: aleksis/core/forms.py:240
 msgid "Write your announcement:"
 msgstr ""
 
-#: aleksis/core/forms.py:277
+#: aleksis/core/forms.py:279
 msgid "You are not allowed to create announcements which are only valid in the past."
 msgstr ""
 
-#: aleksis/core/forms.py:281
+#: aleksis/core/forms.py:283
 msgid "The from date and time must be earlier then the until date and time."
 msgstr ""
 
-#: aleksis/core/forms.py:290
+#: aleksis/core/forms.py:292
 msgid "You need at least one recipient."
 msgstr ""
 
-#: aleksis/core/forms.py:411
+#: aleksis/core/forms.py:401
+msgid "Invitation code"
+msgstr ""
+
+#: aleksis/core/forms.py:402
+msgid "Please enter your invitation code."
+msgstr ""
+
+#: aleksis/core/forms.py:434
 msgid "Who should get the permission?"
 msgstr ""
 
-#: aleksis/core/forms.py:412
+#: aleksis/core/forms.py:435
 msgid "On what?"
 msgstr ""
 
-#: aleksis/core/forms.py:438
+#: aleksis/core/forms.py:461
 msgid "Select objects which the permission should be granted for:"
 msgstr ""
 
-#: aleksis/core/forms.py:441
+#: aleksis/core/forms.py:464
 msgid "Grant the permission for all objects"
 msgstr ""
 
-#: aleksis/core/forms.py:449
+#: aleksis/core/forms.py:472
 msgid "You must select at least one group or person which should get the permission."
 msgstr ""
 
-#: aleksis/core/forms.py:454
+#: aleksis/core/forms.py:477
 msgid "You must grant the permission to all objects and/or to some objects."
 msgstr ""
 
-#: aleksis/core/forms.py:518
-msgid "Account data"
+#: aleksis/core/forms.py:564
+msgid "Adress data"
 msgstr ""
 
-#: aleksis/core/forms.py:524
-msgid "Consents"
+#: aleksis/core/forms.py:576
+msgid "Account data"
 msgstr ""
 
-#: aleksis/core/forms.py:531
+#: aleksis/core/forms.py:583
 msgid "Password"
 msgstr ""
 
-#: aleksis/core/forms.py:537
+#: aleksis/core/forms.py:586
 msgid "Password (again)"
 msgstr ""
 
-#: aleksis/core/forms.py:550
-#, python-brace-format
-msgid "I have read the <a href='{privacy_policy}'>Privacy policy</a> and agree with them."
-msgstr ""
-
-#: aleksis/core/forms.py:575
-msgid "You must type the same password each time."
-msgstr ""
-
-#: aleksis/core/forms.py:720
+#: aleksis/core/forms.py:752
 msgid "No valid selection."
 msgstr ""
 
@@ -240,24 +239,20 @@ msgstr ""
 msgid "There are unresolved data problems."
 msgstr ""
 
-#: aleksis/core/health_checks.py:38
-msgid "The backup folder doesn't exist."
-msgstr ""
-
-#: aleksis/core/health_checks.py:47
+#: aleksis/core/health_checks.py:44
 #, python-brace-format
 msgid "Last backup {time_gone_since_backup}!"
 msgstr ""
 
-#: aleksis/core/health_checks.py:49
+#: aleksis/core/health_checks.py:46
 msgid "No backup found!"
 msgstr ""
 
-#: aleksis/core/health_checks.py:76
+#: aleksis/core/health_checks.py:73
 msgid "No backup result found!"
 msgstr ""
 
-#: aleksis/core/health_checks.py:78
+#: aleksis/core/health_checks.py:75
 #, python-brace-format
 msgid "{task.status} - {task.result}"
 msgstr ""
@@ -273,34 +268,38 @@ msgstr ""
 msgid "Sign up"
 msgstr ""
 
-#: aleksis/core/menus.py:24
+#: aleksis/core/menus.py:24 aleksis/core/templates/invitations/enter.html:7
+msgid "Accept invitation"
+msgstr ""
+
+#: aleksis/core/menus.py:33
 msgid "Dashboard"
 msgstr ""
 
-#: aleksis/core/menus.py:32 aleksis/core/models.py:625
-#: aleksis/core/preferences.py:27
+#: aleksis/core/menus.py:41 aleksis/core/models.py:627
+#: aleksis/core/preferences.py:28
 #: aleksis/core/templates/core/notifications.html:4
 #: aleksis/core/templates/core/notifications.html:5
 msgid "Notifications"
 msgstr ""
 
-#: aleksis/core/menus.py:44
+#: aleksis/core/menus.py:53
 msgid "Account"
 msgstr ""
 
-#: aleksis/core/menus.py:51
+#: aleksis/core/menus.py:60
 msgid "Stop impersonation"
 msgstr ""
 
-#: aleksis/core/menus.py:60 aleksis/core/templates/core/base.html:80
+#: aleksis/core/menus.py:69 aleksis/core/templates/core/base.html:80
 msgid "Logout"
 msgstr ""
 
-#: aleksis/core/menus.py:66
+#: aleksis/core/menus.py:75
 msgid "2FA"
 msgstr ""
 
-#: aleksis/core/menus.py:74
+#: aleksis/core/menus.py:83
 #: aleksis/core/templates/account/password_change.html:5
 #: aleksis/core/templates/account/password_change.html:6
 #: aleksis/core/templates/account/password_change.html:19
@@ -312,830 +311,850 @@ msgstr ""
 msgid "Change password"
 msgstr ""
 
-#: aleksis/core/menus.py:86
+#: aleksis/core/menus.py:95
 msgid "Me"
 msgstr ""
 
-#: aleksis/core/menus.py:95
+#: aleksis/core/menus.py:104
 #: aleksis/core/templates/dynamic_preferences/form.html:5
 msgid "Preferences"
 msgstr ""
 
-#: aleksis/core/menus.py:104
+#: aleksis/core/menus.py:113
 msgid "Third-party accounts"
 msgstr ""
 
-#: aleksis/core/menus.py:113
+#: aleksis/core/menus.py:122
 #: aleksis/core/templates/oauth2_provider/authorized-tokens.html:5
 #: aleksis/core/templates/oauth2_provider/authorized-tokens.html:6
 msgid "Authorized applications"
 msgstr ""
 
-#: aleksis/core/menus.py:124
+#: aleksis/core/menus.py:133
 msgid "Admin"
 msgstr ""
 
-#: aleksis/core/menus.py:132 aleksis/core/models.py:725
+#: aleksis/core/menus.py:141 aleksis/core/models.py:727
 #: aleksis/core/templates/core/announcement/list.html:7
 #: aleksis/core/templates/core/announcement/list.html:8
 msgid "Announcements"
 msgstr ""
 
-#: aleksis/core/menus.py:143 aleksis/core/models.py:126
+#: aleksis/core/menus.py:152 aleksis/core/models.py:130
 #: aleksis/core/templates/core/school_term/list.html:8
 #: aleksis/core/templates/core/school_term/list.html:9
 msgid "School terms"
 msgstr ""
 
-#: aleksis/core/menus.py:154
+#: aleksis/core/menus.py:163
 #: aleksis/core/templates/core/dashboard_widget/list.html:8
 #: aleksis/core/templates/core/dashboard_widget/list.html:9
 msgid "Dashboard widgets"
 msgstr ""
 
-#: aleksis/core/menus.py:165
+#: aleksis/core/menus.py:174
 #: aleksis/core/templates/core/management/data_management.html:6
 #: aleksis/core/templates/core/management/data_management.html:7
 msgid "Data management"
 msgstr ""
 
-#: aleksis/core/menus.py:176
+#: aleksis/core/menus.py:185
 #: aleksis/core/templates/core/pages/system_status.html:5
 #: aleksis/core/templates/core/pages/system_status.html:7
 msgid "System status"
 msgstr ""
 
-#: aleksis/core/menus.py:187
-msgid "Impersonation"
-msgstr ""
-
-#: aleksis/core/menus.py:198
+#: aleksis/core/menus.py:196
 msgid "Configuration"
 msgstr ""
 
-#: aleksis/core/menus.py:209 aleksis/core/templates/core/data_check/list.html:9
+#: aleksis/core/menus.py:207 aleksis/core/templates/core/data_check/list.html:9
 #: aleksis/core/templates/core/data_check/list.html:10
 msgid "Data checks"
 msgstr ""
 
-#: aleksis/core/menus.py:215 aleksis/core/templates/core/perms/list.html:13
+#: aleksis/core/menus.py:213 aleksis/core/templates/core/perms/list.html:13
 #: aleksis/core/templates/core/perms/list.html:14
 msgid "Manage permissions"
 msgstr ""
 
-#: aleksis/core/menus.py:226
+#: aleksis/core/menus.py:224
 msgid "Backend Admin"
 msgstr ""
 
-#: aleksis/core/menus.py:234
+#: aleksis/core/menus.py:232
 #: aleksis/core/templates/oauth2_provider/application/list.html:5
 #: aleksis/core/templates/oauth2_provider/application/list.html:6
 msgid "OAuth2 Applications"
 msgstr ""
 
-#: aleksis/core/menus.py:247
+#: aleksis/core/menus.py:245
 msgid "People"
 msgstr ""
 
-#: aleksis/core/menus.py:278 aleksis/core/models.py:979
+#: aleksis/core/menus.py:276 aleksis/core/models.py:981
 #: aleksis/core/templates/core/group_type/list.html:8
 #: aleksis/core/templates/core/group_type/list.html:9
 msgid "Group types"
 msgstr ""
 
-#: aleksis/core/menus.py:289
+#: aleksis/core/menus.py:287
 msgid "Groups and child groups"
 msgstr ""
 
-#: aleksis/core/menus.py:300 aleksis/core/models.py:460
+#: aleksis/core/menus.py:298 aleksis/core/models.py:462
 #: aleksis/core/templates/core/additional_field/list.html:8
 #: aleksis/core/templates/core/additional_field/list.html:9
 msgid "Additional fields"
 msgstr ""
 
-#: aleksis/core/menus.py:315
+#: aleksis/core/menus.py:309
+msgid "Invite person"
+msgstr ""
+
+#: aleksis/core/menus.py:322
 #: aleksis/core/templates/core/group/child_groups.html:7
 #: aleksis/core/templates/core/group/child_groups.html:9
 msgid "Assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/mixins.py:508
+#: aleksis/core/mixins.py:511
 msgid "Linked school term"
 msgstr ""
 
-#: aleksis/core/models.py:66
+#: aleksis/core/models.py:70
 msgid "Boolean (Yes/No)"
 msgstr ""
 
-#: aleksis/core/models.py:67
+#: aleksis/core/models.py:71
 msgid "Text (one line)"
 msgstr ""
 
-#: aleksis/core/models.py:69
+#: aleksis/core/models.py:73
 msgid "Date and time"
 msgstr ""
 
-#: aleksis/core/models.py:70
+#: aleksis/core/models.py:74
 msgid "Decimal number"
 msgstr ""
 
-#: aleksis/core/models.py:71 aleksis/core/models.py:194
+#: aleksis/core/models.py:75 aleksis/core/models.py:198
 msgid "E-mail address"
 msgstr ""
 
-#: aleksis/core/models.py:72
+#: aleksis/core/models.py:76
 msgid "Integer"
 msgstr ""
 
-#: aleksis/core/models.py:73
+#: aleksis/core/models.py:77
 msgid "IP address"
 msgstr ""
 
-#: aleksis/core/models.py:74
+#: aleksis/core/models.py:78
 msgid "Boolean or empty (Yes/No/Neither)"
 msgstr ""
 
-#: aleksis/core/models.py:75
+#: aleksis/core/models.py:79
 msgid "Text (multi-line)"
 msgstr ""
 
-#: aleksis/core/models.py:77
+#: aleksis/core/models.py:81
 msgid "URL / Link"
 msgstr ""
 
-#: aleksis/core/models.py:89 aleksis/core/models.py:948
+#: aleksis/core/models.py:93 aleksis/core/models.py:950
 msgid "Name"
 msgstr ""
 
-#: aleksis/core/models.py:91
+#: aleksis/core/models.py:95
 msgid "Start date"
 msgstr ""
 
-#: aleksis/core/models.py:92
+#: aleksis/core/models.py:96
 msgid "End date"
 msgstr ""
 
-#: aleksis/core/models.py:111
+#: aleksis/core/models.py:115
 msgid "The start date must be earlier than the end date."
 msgstr ""
 
-#: aleksis/core/models.py:118
+#: aleksis/core/models.py:122
 msgid "There is already a school term for this time or a part of this time."
 msgstr ""
 
-#: aleksis/core/models.py:147 aleksis/core/models.py:897
+#: aleksis/core/models.py:151 aleksis/core/models.py:899
 msgid "Person"
 msgstr ""
 
-#: aleksis/core/models.py:150
+#: aleksis/core/models.py:154
 msgid "Can view address"
 msgstr ""
 
-#: aleksis/core/models.py:151
+#: aleksis/core/models.py:155
 msgid "Can view contact details"
 msgstr ""
 
-#: aleksis/core/models.py:152
+#: aleksis/core/models.py:156
 msgid "Can view photo"
 msgstr ""
 
-#: aleksis/core/models.py:153
+#: aleksis/core/models.py:157
 msgid "Can view persons groups"
 msgstr ""
 
-#: aleksis/core/models.py:154
+#: aleksis/core/models.py:158
 msgid "Can view personal details"
 msgstr ""
 
-#: aleksis/core/models.py:164
+#: aleksis/core/models.py:168
 msgid "female"
 msgstr ""
 
-#: aleksis/core/models.py:164
+#: aleksis/core/models.py:168
 msgid "male"
 msgstr ""
 
-#: aleksis/core/models.py:172
+#: aleksis/core/models.py:176 aleksis/core/models.py:1144
 msgid "Linked user"
 msgstr ""
 
-#: aleksis/core/models.py:174
+#: aleksis/core/models.py:178
 msgid "Is person active?"
 msgstr ""
 
-#: aleksis/core/models.py:176
+#: aleksis/core/models.py:180
 msgid "First name"
 msgstr ""
 
-#: aleksis/core/models.py:177
+#: aleksis/core/models.py:181
 msgid "Last name"
 msgstr ""
 
-#: aleksis/core/models.py:179
+#: aleksis/core/models.py:183
 msgid "Additional name(s)"
 msgstr ""
 
-#: aleksis/core/models.py:183 aleksis/core/models.py:429
+#: aleksis/core/models.py:187 aleksis/core/models.py:431
 msgid "Short name"
 msgstr ""
 
-#: aleksis/core/models.py:186
+#: aleksis/core/models.py:190
 msgid "Street"
 msgstr ""
 
-#: aleksis/core/models.py:187
+#: aleksis/core/models.py:191
 msgid "Street number"
 msgstr ""
 
-#: aleksis/core/models.py:188
+#: aleksis/core/models.py:192
 msgid "Postal code"
 msgstr ""
 
-#: aleksis/core/models.py:189
+#: aleksis/core/models.py:193
 msgid "Place"
 msgstr ""
 
-#: aleksis/core/models.py:191
+#: aleksis/core/models.py:195
 msgid "Home phone"
 msgstr ""
 
-#: aleksis/core/models.py:192
+#: aleksis/core/models.py:196
 msgid "Mobile phone"
 msgstr ""
 
-#: aleksis/core/models.py:196
+#: aleksis/core/models.py:200
 msgid "Date of birth"
 msgstr ""
 
-#: aleksis/core/models.py:198
+#: aleksis/core/models.py:201
 msgid "Place of birth"
 msgstr ""
 
-#: aleksis/core/models.py:200
+#: aleksis/core/models.py:202
 msgid "Sex"
 msgstr ""
 
-#: aleksis/core/models.py:202
+#: aleksis/core/models.py:204
 msgid "Photo"
 msgstr ""
 
-#: aleksis/core/models.py:206 aleksis/core/templates/core/person/full.html:138
+#: aleksis/core/models.py:208 aleksis/core/templates/core/person/full.html:145
 msgid "Guardians / Parents"
 msgstr ""
 
-#: aleksis/core/models.py:213
+#: aleksis/core/models.py:215
 msgid "Primary group"
 msgstr ""
 
-#: aleksis/core/models.py:216 aleksis/core/models.py:583
-#: aleksis/core/models.py:607 aleksis/core/models.py:692
-#: aleksis/core/models.py:972 aleksis/core/templates/core/person/full.html:121
+#: aleksis/core/models.py:218 aleksis/core/models.py:585
+#: aleksis/core/models.py:609 aleksis/core/models.py:694
+#: aleksis/core/models.py:974 aleksis/core/templates/core/person/full.html:128
 msgid "Description"
 msgstr ""
 
-#: aleksis/core/models.py:384
+#: aleksis/core/models.py:386
 msgid "Title of field"
 msgstr ""
 
-#: aleksis/core/models.py:386
+#: aleksis/core/models.py:388
 msgid "Type of field"
 msgstr ""
 
-#: aleksis/core/models.py:393
+#: aleksis/core/models.py:395
 msgid "Addtitional field for groups"
 msgstr ""
 
-#: aleksis/core/models.py:394
+#: aleksis/core/models.py:396
 msgid "Addtitional fields for groups"
 msgstr ""
 
-#: aleksis/core/models.py:414
+#: aleksis/core/models.py:416
 msgid "Can assign child groups to groups"
 msgstr ""
 
-#: aleksis/core/models.py:415
+#: aleksis/core/models.py:417
 msgid "Can view statistics about group."
 msgstr ""
 
-#: aleksis/core/models.py:427
+#: aleksis/core/models.py:429
 msgid "Long name"
 msgstr ""
 
-#: aleksis/core/models.py:437 aleksis/core/templates/core/group/full.html:85
+#: aleksis/core/models.py:439 aleksis/core/templates/core/group/full.html:85
 msgid "Members"
 msgstr ""
 
-#: aleksis/core/models.py:440 aleksis/core/templates/core/group/full.html:82
+#: aleksis/core/models.py:442 aleksis/core/templates/core/group/full.html:82
 msgid "Owners"
 msgstr ""
 
-#: aleksis/core/models.py:447 aleksis/core/templates/core/group/full.html:55
+#: aleksis/core/models.py:449 aleksis/core/templates/core/group/full.html:55
 msgid "Parent groups"
 msgstr ""
 
-#: aleksis/core/models.py:455
+#: aleksis/core/models.py:457
 msgid "Type of group"
 msgstr ""
 
-#: aleksis/core/models.py:582 aleksis/core/models.py:606
-#: aleksis/core/models.py:691
+#: aleksis/core/models.py:584 aleksis/core/models.py:608
+#: aleksis/core/models.py:693
 #: aleksis/core/templates/core/announcement/list.html:18
 msgid "Title"
 msgstr ""
 
-#: aleksis/core/models.py:585
+#: aleksis/core/models.py:587
 msgid "Application"
 msgstr ""
 
-#: aleksis/core/models.py:591
+#: aleksis/core/models.py:593
 msgid "Activity"
 msgstr ""
 
-#: aleksis/core/models.py:592
+#: aleksis/core/models.py:594
 msgid "Activities"
 msgstr ""
 
-#: aleksis/core/models.py:598
+#: aleksis/core/models.py:600
 msgid "Sender"
 msgstr ""
 
-#: aleksis/core/models.py:603
+#: aleksis/core/models.py:605
 msgid "Recipient"
 msgstr ""
 
-#: aleksis/core/models.py:608 aleksis/core/models.py:949
+#: aleksis/core/models.py:610 aleksis/core/models.py:951
 msgid "Link"
 msgstr ""
 
-#: aleksis/core/models.py:610
+#: aleksis/core/models.py:612
 msgid "Read"
 msgstr ""
 
-#: aleksis/core/models.py:611
+#: aleksis/core/models.py:613
 msgid "Sent"
 msgstr ""
 
-#: aleksis/core/models.py:624
+#: aleksis/core/models.py:626
 msgid "Notification"
 msgstr ""
 
-#: aleksis/core/models.py:693
+#: aleksis/core/models.py:695
 msgid "Link to detailed view"
 msgstr ""
 
-#: aleksis/core/models.py:696
+#: aleksis/core/models.py:698
 msgid "Date and time from when to show"
 msgstr ""
 
-#: aleksis/core/models.py:699
+#: aleksis/core/models.py:701
 msgid "Date and time until when to show"
 msgstr ""
 
-#: aleksis/core/models.py:724
+#: aleksis/core/models.py:726
 msgid "Announcement"
 msgstr ""
 
-#: aleksis/core/models.py:762
+#: aleksis/core/models.py:764
 msgid "Announcement recipient"
 msgstr ""
 
-#: aleksis/core/models.py:763
+#: aleksis/core/models.py:765
 msgid "Announcement recipients"
 msgstr ""
 
-#: aleksis/core/models.py:818
+#: aleksis/core/models.py:820
 msgid "Widget Title"
 msgstr ""
 
-#: aleksis/core/models.py:819
+#: aleksis/core/models.py:821
 msgid "Activate Widget"
 msgstr ""
 
-#: aleksis/core/models.py:820
+#: aleksis/core/models.py:822
 msgid "Widget is broken"
 msgstr ""
 
-#: aleksis/core/models.py:823
+#: aleksis/core/models.py:825
 msgid "Size on mobile devices"
 msgstr ""
 
-#: aleksis/core/models.py:824
+#: aleksis/core/models.py:826
 msgid "<= 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:829
+#: aleksis/core/models.py:831
 msgid "Size on tablet devices"
 msgstr ""
 
-#: aleksis/core/models.py:830
+#: aleksis/core/models.py:832
 msgid "> 600 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:835
+#: aleksis/core/models.py:837
 msgid "Size on desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:836
+#: aleksis/core/models.py:838
 msgid "> 992 px, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:841
+#: aleksis/core/models.py:843
 msgid "Size on large desktop devices"
 msgstr ""
 
-#: aleksis/core/models.py:842
+#: aleksis/core/models.py:844
 msgid "> 1200 px>, 12 columns"
 msgstr ""
 
-#: aleksis/core/models.py:873
+#: aleksis/core/models.py:875
 msgid "Can edit default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:874
+#: aleksis/core/models.py:876
 msgid "Dashboard Widget"
 msgstr ""
 
-#: aleksis/core/models.py:875
+#: aleksis/core/models.py:877
 msgid "Dashboard Widgets"
 msgstr ""
 
-#: aleksis/core/models.py:881
+#: aleksis/core/models.py:883
 msgid "URL"
 msgstr ""
 
-#: aleksis/core/models.py:882
+#: aleksis/core/models.py:884
 msgid "Icon URL"
 msgstr ""
 
-#: aleksis/core/models.py:888
+#: aleksis/core/models.py:890
 msgid "External link widget"
 msgstr ""
 
-#: aleksis/core/models.py:889
+#: aleksis/core/models.py:891
 msgid "External link widgets"
 msgstr ""
 
-#: aleksis/core/models.py:894
+#: aleksis/core/models.py:896
 msgid "Dashboard widget"
 msgstr ""
 
-#: aleksis/core/models.py:899
+#: aleksis/core/models.py:901
 msgid "Order"
 msgstr ""
 
-#: aleksis/core/models.py:900
+#: aleksis/core/models.py:902
 msgid "Part of the default dashboard"
 msgstr ""
 
-#: aleksis/core/models.py:915
+#: aleksis/core/models.py:917
 msgid "Dashboard widget order"
 msgstr ""
 
-#: aleksis/core/models.py:916
+#: aleksis/core/models.py:918
 msgid "Dashboard widget orders"
 msgstr ""
 
-#: aleksis/core/models.py:922
+#: aleksis/core/models.py:924
 msgid "Menu ID"
 msgstr ""
 
-#: aleksis/core/models.py:935
+#: aleksis/core/models.py:937
 msgid "Custom menu"
 msgstr ""
 
-#: aleksis/core/models.py:936
+#: aleksis/core/models.py:938
 msgid "Custom menus"
 msgstr ""
 
-#: aleksis/core/models.py:946
+#: aleksis/core/models.py:948
 msgid "Menu"
 msgstr ""
 
-#: aleksis/core/models.py:950
+#: aleksis/core/models.py:952
 msgid "Icon"
 msgstr ""
 
-#: aleksis/core/models.py:956
+#: aleksis/core/models.py:958
 msgid "Custom menu item"
 msgstr ""
 
-#: aleksis/core/models.py:957
+#: aleksis/core/models.py:959
 msgid "Custom menu items"
 msgstr ""
 
-#: aleksis/core/models.py:971
+#: aleksis/core/models.py:973
 msgid "Title of type"
 msgstr ""
 
-#: aleksis/core/models.py:978 aleksis/core/templates/core/group/full.html:47
+#: aleksis/core/models.py:980 aleksis/core/templates/core/group/full.html:47
 msgid "Group type"
 msgstr ""
 
-#: aleksis/core/models.py:992
+#: aleksis/core/models.py:994
 msgid "Can view system status"
 msgstr ""
 
-#: aleksis/core/models.py:993
+#: aleksis/core/models.py:995
 msgid "Can manage data"
 msgstr ""
 
-#: aleksis/core/models.py:994
+#: aleksis/core/models.py:996
 msgid "Can impersonate"
 msgstr ""
 
-#: aleksis/core/models.py:995
+#: aleksis/core/models.py:997
 msgid "Can use search"
 msgstr ""
 
-#: aleksis/core/models.py:996
+#: aleksis/core/models.py:998
 msgid "Can change site preferences"
 msgstr ""
 
-#: aleksis/core/models.py:997
+#: aleksis/core/models.py:999
 msgid "Can change person preferences"
 msgstr ""
 
-#: aleksis/core/models.py:998
+#: aleksis/core/models.py:1000
 msgid "Can change group preferences"
 msgstr ""
 
-#: aleksis/core/models.py:999
+#: aleksis/core/models.py:1001
 msgid "Can test PDF generation"
 msgstr ""
 
-#: aleksis/core/models.py:1035
+#: aleksis/core/models.py:1037
 msgid "Related data check task"
 msgstr ""
 
-#: aleksis/core/models.py:1043
+#: aleksis/core/models.py:1045
 msgid "Issue solved"
 msgstr ""
 
-#: aleksis/core/models.py:1044
+#: aleksis/core/models.py:1046
 msgid "Notification sent"
 msgstr ""
 
-#: aleksis/core/models.py:1057
+#: aleksis/core/models.py:1059
 msgid "Data check result"
 msgstr ""
 
-#: aleksis/core/models.py:1058
+#: aleksis/core/models.py:1060
 msgid "Data check results"
 msgstr ""
 
-#: aleksis/core/models.py:1060
+#: aleksis/core/models.py:1062
 msgid "Can run data checks"
 msgstr ""
 
-#: aleksis/core/models.py:1061
+#: aleksis/core/models.py:1063
 msgid "Can solve data check problems"
 msgstr ""
 
-#: aleksis/core/models.py:1076
+#: aleksis/core/models.py:1070
+msgid "E-Mail address"
+msgstr ""
+
+#: aleksis/core/models.py:1094
 msgid "Owner"
 msgstr ""
 
-#: aleksis/core/models.py:1080
+#: aleksis/core/models.py:1098
 msgid "File expires at"
 msgstr ""
 
-#: aleksis/core/models.py:1082
+#: aleksis/core/models.py:1100
 msgid "Generated HTML file"
 msgstr ""
 
-#: aleksis/core/models.py:1084
+#: aleksis/core/models.py:1102
 msgid "Generated PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1091
+#: aleksis/core/models.py:1109
 msgid "PDF file"
 msgstr ""
 
-#: aleksis/core/models.py:1092
+#: aleksis/core/models.py:1110
 msgid "PDF files"
 msgstr ""
 
-#: aleksis/core/models.py:1097
+#: aleksis/core/models.py:1115
 msgid "Task result"
 msgstr ""
 
-#: aleksis/core/models.py:1100
+#: aleksis/core/models.py:1118
 msgid "Task user"
 msgstr ""
 
-#: aleksis/core/models.py:1112
+#: aleksis/core/models.py:1130
 msgid "Task user assignment"
 msgstr ""
 
-#: aleksis/core/models.py:1113
+#: aleksis/core/models.py:1131
 msgid "Task user assignments"
 msgstr ""
 
-#: aleksis/core/models.py:1127
+#: aleksis/core/models.py:1147
+msgid "Additional attributes"
+msgstr ""
+
+#: aleksis/core/models.py:1185
 msgid "Allowed scopes that clients can request"
 msgstr ""
 
-#: aleksis/core/preferences.py:23
+#: aleksis/core/preferences.py:24
 msgid "General"
 msgstr ""
 
-#: aleksis/core/preferences.py:24
+#: aleksis/core/preferences.py:25
 msgid "School"
 msgstr ""
 
-#: aleksis/core/preferences.py:25
+#: aleksis/core/preferences.py:26
 msgid "Theme"
 msgstr ""
 
-#: aleksis/core/preferences.py:26
+#: aleksis/core/preferences.py:27
 msgid "Mail"
 msgstr ""
 
-#: aleksis/core/preferences.py:28
+#: aleksis/core/preferences.py:29
 msgid "Footer"
 msgstr ""
 
-#: aleksis/core/preferences.py:29
+#: aleksis/core/preferences.py:30
 msgid "Accounts"
 msgstr ""
 
-#: aleksis/core/preferences.py:30
+#: aleksis/core/preferences.py:31
 msgid "Authentication"
 msgstr ""
 
-#: aleksis/core/preferences.py:31
+#: aleksis/core/preferences.py:32
 msgid "Internationalisation"
 msgstr ""
 
-#: aleksis/core/preferences.py:42
+#: aleksis/core/preferences.py:43
 msgid "Site title"
 msgstr ""
 
-#: aleksis/core/preferences.py:53
+#: aleksis/core/preferences.py:54
 msgid "Site description"
 msgstr ""
 
-#: aleksis/core/preferences.py:64
+#: aleksis/core/preferences.py:65
 msgid "Primary colour"
 msgstr ""
 
-#: aleksis/core/preferences.py:76
+#: aleksis/core/preferences.py:77
 msgid "Secondary colour"
 msgstr ""
 
-#: aleksis/core/preferences.py:87
+#: aleksis/core/preferences.py:88
 msgid "Logo"
 msgstr ""
 
-#: aleksis/core/preferences.py:97
+#: aleksis/core/preferences.py:98
 msgid "Favicon"
 msgstr ""
 
-#: aleksis/core/preferences.py:107
+#: aleksis/core/preferences.py:108
 msgid "PWA-Icon"
 msgstr ""
 
-#: aleksis/core/preferences.py:118
+#: aleksis/core/preferences.py:119
 msgid "Mail out name"
 msgstr ""
 
-#: aleksis/core/preferences.py:129
+#: aleksis/core/preferences.py:130
 msgid "Mail out address"
 msgstr ""
 
-#: aleksis/core/preferences.py:141
+#: aleksis/core/preferences.py:142
 msgid "Link to privacy policy"
 msgstr ""
 
-#: aleksis/core/preferences.py:153
+#: aleksis/core/preferences.py:154
 msgid "Link to imprint"
 msgstr ""
 
-#: aleksis/core/preferences.py:165
+#: aleksis/core/preferences.py:166
 msgid "Name format for addressing"
 msgstr ""
 
-#: aleksis/core/preferences.py:181
+#: aleksis/core/preferences.py:182
 msgid "Channels to use for notifications"
 msgstr ""
 
-#: aleksis/core/preferences.py:193
+#: aleksis/core/preferences.py:194
 msgid "Regular expression to match primary group, e.g. '^Class .*'"
 msgstr ""
 
-#: aleksis/core/preferences.py:204
+#: aleksis/core/preferences.py:205
 msgid "Field on person to match primary group against"
 msgstr ""
 
-#: aleksis/core/preferences.py:216
+#: aleksis/core/preferences.py:217
 msgid "Automatically create new persons for new users"
 msgstr ""
 
-#: aleksis/core/preferences.py:225
+#: aleksis/core/preferences.py:226
 msgid "Automatically link existing persons to new users by their e-mail address"
 msgstr ""
 
-#: aleksis/core/preferences.py:236
+#: aleksis/core/preferences.py:237
 msgid "Display name of the school"
 msgstr ""
 
-#: aleksis/core/preferences.py:247
+#: aleksis/core/preferences.py:248
 msgid "Official name of the school, e.g. as given by supervisory authority"
 msgstr ""
 
-#: aleksis/core/preferences.py:255
+#: aleksis/core/preferences.py:256
 msgid "Allow users to change their passwords"
 msgstr ""
 
-#: aleksis/core/preferences.py:263
+#: aleksis/core/preferences.py:264
 msgid "Enable signup"
 msgstr ""
 
-#: aleksis/core/preferences.py:274
+#: aleksis/core/preferences.py:272
+msgid "Enable invitations"
+msgstr ""
+
+#: aleksis/core/preferences.py:280
+msgid "Length of invite code. (Default 3: abcde-acbde-abcde)"
+msgstr ""
+
+#: aleksis/core/preferences.py:288
+msgid "Size of packets. (Default 5: abcde)"
+msgstr ""
+
+#: aleksis/core/preferences.py:298
 msgid "Allowed Grant Flows for OAuth applications"
 msgstr ""
 
-#: aleksis/core/preferences.py:287
+#: aleksis/core/preferences.py:311
 msgid "Available languages"
 msgstr ""
 
-#: aleksis/core/preferences.py:299
+#: aleksis/core/preferences.py:323
 msgid "Send emails if data checks detect problems"
 msgstr ""
 
-#: aleksis/core/preferences.py:310
+#: aleksis/core/preferences.py:334
 msgid "Email recipients for data checks problem emails"
 msgstr ""
 
-#: aleksis/core/preferences.py:321
+#: aleksis/core/preferences.py:345
 msgid "Email recipient groups for data checks problem emails"
 msgstr ""
 
-#: aleksis/core/preferences.py:330
+#: aleksis/core/preferences.py:354
 msgid "Show dashboard to users without login"
 msgstr ""
 
-#: aleksis/core/preferences.py:339
+#: aleksis/core/preferences.py:363
 msgid "Allow users to edit their dashboard"
 msgstr ""
 
-#: aleksis/core/preferences.py:350
+#: aleksis/core/preferences.py:374
 msgid "Fields on person model which are editable by themselves."
 msgstr ""
 
-#: aleksis/core/preferences.py:364
+#: aleksis/core/preferences.py:388
 msgid "Editable fields on person model which should trigger a notification on change"
 msgstr ""
 
-#: aleksis/core/preferences.py:377
+#: aleksis/core/preferences.py:401
 msgid "Contact for notification if a person changes their data"
 msgstr ""
 
-#: aleksis/core/preferences.py:387
+#: aleksis/core/preferences.py:411
 msgid "PDF file expiration duration"
 msgstr ""
 
-#: aleksis/core/preferences.py:388
+#: aleksis/core/preferences.py:412
 msgid "in minutes"
 msgstr ""
 
-#: aleksis/core/preferences.py:398
+#: aleksis/core/preferences.py:422
 msgid "Automatically update the dashboard and its widgets"
 msgstr ""
 
-#: aleksis/core/preferences.py:408
+#: aleksis/core/preferences.py:432
 msgid "Automatically update the dashboard and its widgets sitewide"
 msgstr ""
 
-#: aleksis/core/settings.py:474
+#: aleksis/core/settings.py:507
 msgid "English"
 msgstr ""
 
-#: aleksis/core/settings.py:475
+#: aleksis/core/settings.py:508
 msgid "German"
 msgstr ""
 
-#: aleksis/core/tables.py:19
+#: aleksis/core/tables.py:24
 #: aleksis/core/templates/core/announcement/list.html:36
 #: aleksis/core/templates/core/group/full.html:24
-#: aleksis/core/templates/core/person/full.html:23
+#: aleksis/core/templates/core/person/full.html:24
 #: aleksis/core/templates/oauth2_provider/application/detail.html:17
 msgid "Edit"
 msgstr ""
 
-#: aleksis/core/tables.py:21 aleksis/core/tables.py:89
-#: aleksis/core/tables.py:105
+#: aleksis/core/tables.py:26 aleksis/core/tables.py:94
+#: aleksis/core/tables.py:137
 #: aleksis/core/templates/core/announcement/list.html:22
 msgid "Actions"
 msgstr ""
 
-#: aleksis/core/tables.py:56 aleksis/core/tables.py:57
-#: aleksis/core/tables.py:71 aleksis/core/tables.py:87
-#: aleksis/core/tables.py:103
+#: aleksis/core/tables.py:61 aleksis/core/tables.py:62
+#: aleksis/core/tables.py:76 aleksis/core/tables.py:92
+#: aleksis/core/tables.py:135
 #: aleksis/core/templates/core/announcement/list.html:42
 #: aleksis/core/templates/core/group/full.html:31
 #: aleksis/core/templates/core/pages/delete.html:22
-#: aleksis/core/templates/core/person/full.html:30
+#: aleksis/core/templates/core/person/full.html:31
 #: aleksis/core/templates/oauth2_provider/application/detail.html:21
 msgid "Delete"
 msgstr ""
@@ -1207,11 +1226,11 @@ msgstr ""
 msgid "Account inactive"
 msgstr ""
 
-#: aleksis/core/templates/account/account_inactive.html:13
+#: aleksis/core/templates/account/account_inactive.html:14
 msgid "Account inactive."
 msgstr ""
 
-#: aleksis/core/templates/account/account_inactive.html:15
+#: aleksis/core/templates/account/account_inactive.html:17
 msgid ""
 "\n"
 "            This account is currently inactive. If you think this is an\n"
@@ -1363,11 +1382,11 @@ msgstr ""
 msgid "Signup closed"
 msgstr ""
 
-#: aleksis/core/templates/account/signup_closed.html:13
+#: aleksis/core/templates/account/signup_closed.html:14
 msgid "Signup closed."
 msgstr ""
 
-#: aleksis/core/templates/account/signup_closed.html:15
+#: aleksis/core/templates/account/signup_closed.html:17
 msgid ""
 "\n"
 "            This sign up is currently closed. If you think this is an\n"
@@ -1405,11 +1424,6 @@ msgid ""
 "          "
 msgstr ""
 
-#: aleksis/core/templates/account/verification_sent.html:30
-#, python-format
-msgid "<strong>Note:</strong> you can still <a href=\"%(email_url)s\">change your e-mail address</a>"
-msgstr ""
-
 #: aleksis/core/templates/core/additional_field/edit.html:6
 #: aleksis/core/templates/core/additional_field/edit.html:7
 msgid "Edit additional field"
@@ -1458,11 +1472,11 @@ msgid "Logged in as"
 msgstr ""
 
 #: aleksis/core/templates/core/base.html:175
-msgid "About AlekSIS — The Free School Information System"
+msgid "About AlekSIS® — The Free School Information System"
 msgstr ""
 
 #: aleksis/core/templates/core/base.html:183
-msgid "Impress"
+msgid "Imprint"
 msgstr ""
 
 #: aleksis/core/templates/core/base.html:191
@@ -1470,7 +1484,7 @@ msgid "Privacy Policy"
 msgstr ""
 
 #: aleksis/core/templates/core/base_print.html:72
-msgid "Powered by AlekSIS"
+msgid "Powered by AlekSIS®"
 msgstr ""
 
 #: aleksis/core/templates/core/dashboard_widget/create.html:8
@@ -1685,7 +1699,7 @@ msgid "Edit group"
 msgstr ""
 
 #: aleksis/core/templates/core/group/full.html:38
-#: aleksis/core/templates/core/person/full.html:37
+#: aleksis/core/templates/core/person/full.html:38
 msgid "Change preferences"
 msgstr ""
 
@@ -1774,36 +1788,46 @@ msgid "No notifications available yet."
 msgstr ""
 
 #: aleksis/core/templates/core/pages/about.html:6
-#: aleksis/core/templates/core/pages/about.html:15
-msgid "About AlekSIS"
+msgid "About AlekSIS®"
 msgstr ""
 
 #: aleksis/core/templates/core/pages/about.html:7
-msgid "AlekSIS – The Free School Information System"
+msgid "AlekSIS® – The Free School Information System"
+msgstr ""
+
+#: aleksis/core/templates/core/pages/about.html:15
+msgid "About AlekSIS"
 msgstr ""
 
 #: aleksis/core/templates/core/pages/about.html:17
 msgid ""
 "\n"
-"              This platform is powered by AlekSIS, a web-based school information system (SIS) which can be used\n"
+"              This platform is powered by AlekSIS®, a web-based school information system (SIS) which can be used\n"
 "              to manage and/or publish organisational artifacts of educational institutions. AlekSIS is free software and\n"
 "              can be used by anyone.\n"
 "            "
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:25
+#: aleksis/core/templates/core/pages/about.html:24
+msgid ""
+"\n"
+"              AlekSIS® is a registered trademark of the AlekSIS open source project, represented by Teckids e.V.\n"
+"            "
+msgstr ""
+
+#: aleksis/core/templates/core/pages/about.html:30
 msgid "Website of AlekSIS"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:26
+#: aleksis/core/templates/core/pages/about.html:31
 msgid "Source code"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:35
+#: aleksis/core/templates/core/pages/about.html:40
 msgid "Licence information"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:37
+#: aleksis/core/templates/core/pages/about.html:42
 msgid ""
 "\n"
 "              The core and the official apps of AlekSIS are licenced under the EUPL, version 1.2 or later. For licence\n"
@@ -1812,23 +1836,23 @@ msgid ""
 "            "
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:45
+#: aleksis/core/templates/core/pages/about.html:50
 msgid "Free/Open Source Licence"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:46
+#: aleksis/core/templates/core/pages/about.html:51
 msgid "Other Licence"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:50
+#: aleksis/core/templates/core/pages/about.html:55
 msgid "Full licence text"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:51
+#: aleksis/core/templates/core/pages/about.html:56
 msgid "More information about the EUPL"
 msgstr ""
 
-#: aleksis/core/templates/core/pages/about.html:90
+#: aleksis/core/templates/core/pages/about.html:95
 #, python-format
 msgid ""
 "\n"
@@ -2069,17 +2093,19 @@ msgstr ""
 msgid "Edit person"
 msgstr ""
 
-#: aleksis/core/templates/core/person/full.html:44
-#: aleksis/core/templates/impersonate/list_users.html:7
-#: aleksis/core/templates/impersonate/list_users.html:8
+#: aleksis/core/templates/core/person/full.html:45
 msgid "Impersonate"
 msgstr ""
 
-#: aleksis/core/templates/core/person/full.html:50
+#: aleksis/core/templates/core/person/full.html:51
+msgid "Invite user"
+msgstr ""
+
+#: aleksis/core/templates/core/person/full.html:57
 msgid "Contact details"
 msgstr ""
 
-#: aleksis/core/templates/core/person/full.html:131
+#: aleksis/core/templates/core/person/full.html:138
 msgid "Children"
 msgstr ""
 
@@ -2119,6 +2145,49 @@ msgstr ""
 msgid "Save preferences"
 msgstr ""
 
+#: aleksis/core/templates/invitations/enter.html:21
+msgid "Accept your invitation"
+msgstr ""
+
+#: aleksis/core/templates/invitations/enter.html:25
+msgid ""
+"\n"
+"                Please enter your invitation code to register\n"
+"                your new user account:\n"
+"              "
+msgstr ""
+
+#: aleksis/core/templates/invitations/enter.html:37
+msgid "Accept invite"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:9
+#: aleksis/core/templates/invitations/forms/_invite.html:10
+#: aleksis/core/templates/invitations/forms/_invite.html:21
+msgid "Invite"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:17
+msgid "Invite by email address"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:26
+msgid "Generate invitation code"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:29
+msgid "Generate code"
+msgstr ""
+
+#: aleksis/core/templates/invitations/forms/_invite.html:33
+msgid "Invitations"
+msgstr ""
+
+#: aleksis/core/templates/invitations/messages/invite_accepted.txt:3
+#, python-format
+msgid "The invitation for %(email)s has been accepted."
+msgstr ""
+
 #: aleksis/core/templates/oauth2_provider/application/create.html:5
 #: aleksis/core/templates/oauth2_provider/application/create.html:6
 msgid "Register OAuth2 Application"
@@ -2775,147 +2844,160 @@ msgstr ""
 msgid "SMS"
 msgstr ""
 
-#: aleksis/core/util/pdf.py:105
+#: aleksis/core/util/pdf.py:113
 msgid "Progress: Generate PDF file"
 msgstr ""
 
-#: aleksis/core/util/pdf.py:106
+#: aleksis/core/util/pdf.py:114
 msgid "Generating PDF file …"
 msgstr ""
 
-#: aleksis/core/util/pdf.py:107
+#: aleksis/core/util/pdf.py:115
 msgid "The PDF file has been generated successfully."
 msgstr ""
 
-#: aleksis/core/util/pdf.py:108
+#: aleksis/core/util/pdf.py:116
 msgid "There was a problem while generating the PDF file."
 msgstr ""
 
-#: aleksis/core/util/pdf.py:111
+#: aleksis/core/util/pdf.py:119
 msgid "Download PDF"
 msgstr ""
 
-#: aleksis/core/views.py:270
+#: aleksis/core/views.py:280
 msgid "The school term has been created."
 msgstr ""
 
-#: aleksis/core/views.py:282
+#: aleksis/core/views.py:292
 msgid "The school term has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:406
+#: aleksis/core/views.py:416
 msgid "The child groups were successfully saved."
 msgstr ""
 
-#: aleksis/core/views.py:425 aleksis/core/views.py:435
+#: aleksis/core/views.py:435 aleksis/core/views.py:445
 msgid "The person has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:485
+#: aleksis/core/views.py:495
 msgid "The group has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:582
+#: aleksis/core/views.py:592
 msgid "The announcement has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:598
+#: aleksis/core/views.py:608
 msgid "The announcement has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:682
+#: aleksis/core/views.py:695
 msgid "The preferences have been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:706
+#: aleksis/core/views.py:719
 msgid "The person has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:720
+#: aleksis/core/views.py:733
 msgid "The group has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:752
+#: aleksis/core/views.py:765
 msgid "The additional_field has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:786
+#: aleksis/core/views.py:799
 msgid "The additional field has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:811
+#: aleksis/core/views.py:824
 msgid "The group type has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:841
+#: aleksis/core/views.py:854
 msgid "The group type has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:874
+#: aleksis/core/views.py:887
 msgid "Progress: Run data checks"
 msgstr ""
 
-#: aleksis/core/views.py:875
+#: aleksis/core/views.py:888
 msgid "Run data checks …"
 msgstr ""
 
-#: aleksis/core/views.py:876
+#: aleksis/core/views.py:889
 msgid "The data checks were run successfully."
 msgstr ""
 
-#: aleksis/core/views.py:877
+#: aleksis/core/views.py:890
 msgid "There was a problem while running data checks."
 msgstr ""
 
-#: aleksis/core/views.py:893
+#: aleksis/core/views.py:906
 #, python-brace-format
 msgid "The solve option '{solve_option_obj.verbose_name}' "
 msgstr ""
 
-#: aleksis/core/views.py:935
+#: aleksis/core/views.py:948
 msgid "The dashboard widget has been saved."
 msgstr ""
 
-#: aleksis/core/views.py:965
+#: aleksis/core/views.py:978
 msgid "The dashboard widget has been created."
 msgstr ""
 
-#: aleksis/core/views.py:975
+#: aleksis/core/views.py:988
 msgid "The dashboard widget has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1042
+#: aleksis/core/views.py:1055
 msgid "Your dashboard configuration has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1044
+#: aleksis/core/views.py:1057
 msgid "The configuration of the default dashboard has been saved successfully."
 msgstr ""
 
-#: aleksis/core/views.py:1139
+#: aleksis/core/views.py:1127
+#, python-brace-format
+msgid "The invitation was successfully created. The invitation code is {code}"
+msgstr ""
+
+#: aleksis/core/views.py:1218
 msgid "We have successfully assigned the permissions."
 msgstr ""
 
-#: aleksis/core/views.py:1149
+#: aleksis/core/views.py:1228
 msgid "The global user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1159
+#: aleksis/core/views.py:1238
 msgid "The global group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1169
+#: aleksis/core/views.py:1248
 msgid "The object user permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1179
+#: aleksis/core/views.py:1258
 msgid "The object group permission has been deleted."
 msgstr ""
 
-#: aleksis/core/views.py:1298
+#: aleksis/core/views.py:1377
 msgid "The third-party account could not be disconnected because it is the only login method available."
 msgstr ""
 
-#: aleksis/core/views.py:1305
+#: aleksis/core/views.py:1384
 msgid "The third-party account has been successfully disconnected."
 msgstr ""
+
+#: aleksis/core/views.py:1441
+msgid "Person was invited successfully."
+msgstr ""
+
+#: aleksis/core/views.py:1443
+msgid "Person was already invited."
+msgstr ""
diff --git a/aleksis/core/locale/tr_TR/LC_MESSAGES/djangojs.po b/aleksis/core/locale/tr_TR/LC_MESSAGES/djangojs.po
index 2458849847f712fe1cf63743640716c5b3ef561a..d67bdb0055bf1ee96915909ce9e21023085c466b 100644
--- a/aleksis/core/locale/tr_TR/LC_MESSAGES/djangojs.po
+++ b/aleksis/core/locale/tr_TR/LC_MESSAGES/djangojs.po
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2021-11-29 09:59+0100\n"
+"POT-Creation-Date: 2021-12-28 12:14+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -29,6 +29,6 @@ msgstr ""
 msgid "OK"
 msgstr ""
 
-#: aleksis/core/static/js/main.js:127
+#: aleksis/core/static/js/main.js:128
 msgid "This page may contain outdated information since there is no internet connection."
 msgstr ""
diff --git a/aleksis/core/managers.py b/aleksis/core/managers.py
index aed4cab72d4b0cd53d3544b20f0180a7087dfd2c..7a549775f1730d57f83e6c3486644fd2c3bdda65 100644
--- a/aleksis/core/managers.py
+++ b/aleksis/core/managers.py
@@ -7,6 +7,7 @@ from django.db.models import QuerySet
 from django.db.models.manager import Manager
 
 from calendarweek import CalendarWeek
+from django_cte import CTEManager, CTEQuerySet
 from polymorphic.managers import PolymorphicManager
 
 
@@ -84,15 +85,15 @@ class SchoolTermRelatedQuerySet(QuerySet):
             return None
 
 
-class GroupManager(CurrentSiteManagerWithoutMigrations):
+class GroupManager(CurrentSiteManagerWithoutMigrations, CTEManager):
     """Manager adding specific methods to groups."""
 
     def get_queryset(self):
         """Ensure all related data is loaded as well."""
-        return super().get_queryset().select_related("school_term")
+        return Manager.get_queryset(self).select_related("school_term")
 
 
-class GroupQuerySet(SchoolTermRelatedQuerySet):
+class GroupQuerySet(SchoolTermRelatedQuerySet, CTEQuerySet):
     pass
 
 
diff --git a/aleksis/core/menus.py b/aleksis/core/menus.py
index 924e34af935c9b171dba20a3389afa6c775adaa2..7938539c53327936d86d6c08334834e8e2a4fb0a 100644
--- a/aleksis/core/menus.py
+++ b/aleksis/core/menus.py
@@ -20,6 +20,15 @@ MENUS = {
                 ("aleksis.core.util.predicates.permission_validator", "core.can_register"),
             ],
         },
+        {
+            "name": _("Accept invitation"),
+            "url": "enter_invitation_code",
+            "icon": "vpn_key",
+            "validators": [
+                "menu_generator.validators.is_anonymous",
+                ("aleksis.core.util.predicates.permission_validator", "core.invite_enabled"),
+            ],
+        },
         {
             "name": _("Dashboard"),
             "url": "index",
@@ -183,17 +192,6 @@ MENUS = {
                         ),
                     ],
                 },
-                {
-                    "name": _("Impersonation"),
-                    "url": "impersonate-list",
-                    "icon": "people",
-                    "validators": [
-                        (
-                            "aleksis.core.util.predicates.permission_validator",
-                            "core.impersonate_rule",
-                        ),
-                    ],
-                },
                 {
                     "name": _("Configuration"),
                     "url": "preferences_site",
@@ -307,6 +305,15 @@ MENUS = {
                         )
                     ],
                 },
+                {
+                    "name": _("Invite person"),
+                    "url": "invite_person",
+                    "icon": "card_giftcard",
+                    "validators": [
+                        "menu_generator.validators.is_authenticated",
+                        ("aleksis.core.util.predicates.permission_validator", "core.can_invite"),
+                    ],
+                },
             ],
         },
     ],
diff --git a/aleksis/core/migrations/0028_char_field_not_null.py b/aleksis/core/migrations/0028_char_field_not_null.py
new file mode 100644
index 0000000000000000000000000000000000000000..76c715e03f35d94d92ed26a67b7796f6901b8f10
--- /dev/null
+++ b/aleksis/core/migrations/0028_char_field_not_null.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.2.10 on 2021-12-15 21:10
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0027_person_place_of_birth'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='oauthapplication',
+            name='authorization_grant_type',
+            field=models.CharField(blank=True, choices=[('authorization-code', 'Authorization code'), ('implicit', 'Implicit'), ('password', 'Resource owner password-based'), ('client-credentials', 'Client credentials'), ('openid-hybrid', 'OpenID connect hybrid')], default='', max_length=32),
+            preserve_default=False,
+        ),
+        migrations.AlterField(
+            model_name='person',
+            name='place_of_birth',
+            field=models.CharField(blank=True, default='', max_length=255, verbose_name='Place of birth'),
+            preserve_default=False,
+        ),
+    ]
diff --git a/aleksis/core/migrations/0029_invitations.py b/aleksis/core/migrations/0029_invitations.py
new file mode 100644
index 0000000000000000000000000000000000000000..0c4410267bf7064dbb0b8c9b57ff7f15a9645e93
--- /dev/null
+++ b/aleksis/core/migrations/0029_invitations.py
@@ -0,0 +1,34 @@
+# Generated by Django 3.1.7 on 2021-03-31 16:55
+
+import aleksis.core.mixins
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('core', '0028_char_field_not_null'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='PersonInvitation',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('accepted', models.BooleanField(default=False, verbose_name='accepted')),
+                ('key', models.CharField(max_length=64, unique=True, verbose_name='key')),
+                ('sent', models.DateTimeField(null=True, verbose_name='sent')),
+                ('email', models.EmailField(blank=True, max_length=254, verbose_name='E-Mail address')),
+                ('inviter', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+                ('person', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='invitation', to='core.person'))
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=(models.Model, aleksis.core.mixins.PureDjangoModel),
+        ),
+    ]
diff --git a/aleksis/core/migrations/0030_user_attributes.py b/aleksis/core/migrations/0030_user_attributes.py
new file mode 100644
index 0000000000000000000000000000000000000000..38434842aae54f5f4ad004094c33c524dd0a69ba
--- /dev/null
+++ b/aleksis/core/migrations/0030_user_attributes.py
@@ -0,0 +1,46 @@
+# Generated by Django 3.2.10 on 2021-12-25 10:59
+
+import aleksis.core.mixins
+from django.conf import settings
+from django.contrib.auth import get_user_model
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+def assume_ldap_authenticated_true(apps, schema_editor):
+    """Set ldap_authenticated user attribute to True to protect existing sites."""
+    if not hasattr(settings, "AUTH_LDAP_SERVER_URI"):
+        # Skip if LDAP is not used on site
+        return
+
+    User = get_user_model()
+    UserAdditionalAttributes = apps.get_model("core", "UserAdditionalAttributes")
+
+    db_alias = schema_editor.connection.alias
+
+    attributes = [
+        UserAdditionalAttributes(user_id=user.pk, attributes={"ldap_authenticated": True})
+        for user in User.objects.using(db_alias).all()
+    ]
+    UserAdditionalAttributes.objects.using(db_alias).bulk_create(attributes)
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ('core', '0029_invitations'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='UserAdditionalAttributes',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('attributes', models.JSONField(default=dict, verbose_name='Additional attributes')),
+                ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='additional_attributes', to=settings.AUTH_USER_MODEL, verbose_name='Linked user')),
+            ],
+            bases=(models.Model, aleksis.core.mixins.PureDjangoModel),
+        ),
+        migrations.RunPython(assume_ldap_authenticated_true, lambda a, s: None),
+    ]
diff --git a/aleksis/core/migrations/0031_oauthapplication_icon.py b/aleksis/core/migrations/0031_oauthapplication_icon.py
new file mode 100644
index 0000000000000000000000000000000000000000..26f2d6575e768578e653088c582fd3dadd147dc5
--- /dev/null
+++ b/aleksis/core/migrations/0031_oauthapplication_icon.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.11 on 2022-01-08 13:59
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0030_user_attributes'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='oauthapplication',
+            name='icon',
+            field=models.ImageField(blank=True, help_text='This image will be shown as icon in the authorization flow. It should be squared.', null=True, upload_to='', verbose_name='Icon'),
+        ),
+    ]
diff --git a/aleksis/core/migrations/0031_remove_person_is_active.py b/aleksis/core/migrations/0031_remove_person_is_active.py
new file mode 100644
index 0000000000000000000000000000000000000000..a3c3f7a444964a62f7b69a293e9a452976f0300d
--- /dev/null
+++ b/aleksis/core/migrations/0031_remove_person_is_active.py
@@ -0,0 +1,17 @@
+# Generated by Django 3.2.11 on 2022-01-08 10:30
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0030_user_attributes'),
+    ]
+
+    operations = [
+        migrations.RemoveField(
+            model_name='person',
+            name='is_active',
+        ),
+    ]
diff --git a/aleksis/core/mixins.py b/aleksis/core/mixins.py
index 3232aac4deb912dfb65f28d69831767bf2fd8445..c6cee9a8c6bbefed3439eef156b685f5484e4a13 100644
--- a/aleksis/core/mixins.py
+++ b/aleksis/core/mixins.py
@@ -1,5 +1,6 @@
 # flake8: noqa: DJ12
 
+import os
 from datetime import datetime
 from typing import Any, Callable, List, Optional, Union
 
@@ -21,6 +22,8 @@ from django.views.generic import CreateView, UpdateView
 from django.views.generic.edit import DeleteView, ModelFormMixin
 
 import reversion
+from dynamic_preferences.settings import preferences_settings
+from dynamic_preferences.types import FilePreference
 from guardian.admin import GuardedModelAdmin
 from guardian.core import ObjectPermissionChecker
 from jsonstore.fields import IntegerField, JSONFieldMixin
@@ -529,3 +532,14 @@ class SchoolTermRelatedExtensibleForm(ExtensibleForm):
             kwargs["initial"] = {"school_term": SchoolTerm.current}
 
         super().__init__(*args, **kwargs)
+
+
+class PublicFilePreferenceMixin(FilePreference):
+    """Uploads a file to the public namespace."""
+
+    upload_path = "public"
+
+    def get_upload_path(self):
+        return os.path.join(
+            self.upload_path, preferences_settings.FILE_PREFERENCE_UPLOAD_DIR, self.identifier()
+        )
diff --git a/aleksis/core/models.py b/aleksis/core/models.py
index 1176c743b53ca432ca5d800245b8b1d4cb67ecae..cac2d9d950e9d968983584ab3dbd0bf59405269b 100644
--- a/aleksis/core/models.py
+++ b/aleksis/core/models.py
@@ -1,7 +1,7 @@
 # flake8: noqa: DJ01
 import hmac
 from datetime import date, datetime, timedelta
-from typing import Iterable, List, Optional, Sequence, Union
+from typing import Any, Iterable, List, Optional, Sequence, Union
 from urllib.parse import urlparse
 
 from django.conf import settings
@@ -14,7 +14,7 @@ from django.contrib.sites.models import Site
 from django.core.exceptions import ValidationError
 from django.core.validators import MaxValueValidator
 from django.db import models, transaction
-from django.db.models import QuerySet
+from django.db.models import Q, QuerySet
 from django.db.models.signals import m2m_changed
 from django.dispatch import receiver
 from django.forms.widgets import Media
@@ -28,8 +28,13 @@ import jsonstore
 from cachalot.api import cachalot_disabled
 from cache_memoize import cache_memoize
 from django_celery_results.models import TaskResult
+from django_cte import CTEQuerySet, With
 from dynamic_preferences.models import PerInstancePreferenceModel
 from image_cropping import ImageCropField, ImageRatioField
+from invitations import signals
+from invitations.adapters import get_invitations_adapter
+from invitations.base_invitation import AbstractBaseInvitation
+from invitations.models import Invitation
 from model_utils import FieldTracker
 from model_utils.models import TimeStampedModel
 from oauth2_provider.models import (
@@ -60,7 +65,7 @@ from .mixins import (
     SchoolTermRelatedExtensibleModel,
 )
 from .tasks import send_notification
-from .util.core_helpers import get_site_preferences, now_tomorrow
+from .util.core_helpers import generate_random_code, get_site_preferences, now_tomorrow
 from .util.model_helpers import ICONS
 
 FIELD_CHOICES = (
@@ -172,7 +177,6 @@ class Person(ExtensibleModel):
         related_name="person",
         verbose_name=_("Linked user"),
     )
-    is_active = models.BooleanField(verbose_name=_("Is person active?"), default=True)
 
     first_name = models.CharField(verbose_name=_("First name"), max_length=255)
     last_name = models.CharField(verbose_name=_("Last name"), max_length=255)
@@ -195,9 +199,7 @@ class Person(ExtensibleModel):
     email = models.EmailField(verbose_name=_("E-mail address"), blank=True)
 
     date_of_birth = models.DateField(verbose_name=_("Date of birth"), blank=True, null=True)
-    place_of_birth = models.CharField(
-        verbose_name=_("Place of birth"), max_length=255, blank=True, null=True
-    )
+    place_of_birth = models.CharField(verbose_name=_("Place of birth"), max_length=255, blank=True)
     sex = models.CharField(verbose_name=_("Sex"), max_length=1, choices=SEX_CHOICES, blank=True)
 
     photo = ImageCropField(
@@ -316,6 +318,22 @@ class Person(ExtensibleModel):
 
     user_info_tracker = FieldTracker(fields=("first_name", "last_name", "email"))
 
+    @property
+    def member_of_recursive(self) -> QuerySet:
+        """Get all groups this person is a member of, recursively."""
+        q = self.member_of
+        for group in q.all():
+            q = q.union(group.parent_groups_recursive)
+        return q
+
+    @property
+    def owner_of_recursive(self) -> QuerySet:
+        """Get all groups this person is a member of, recursively."""
+        q = self.owner_of
+        for group in q.all():
+            q = q.union(group.child_groups_recursive)
+        return q
+
     def save(self, *args, **kwargs):
         # Determine all fields that were changed since last load
         dirty = self.pk is None or bool(self.user_info_tracker.changed())
@@ -523,6 +541,50 @@ class Group(SchoolTermRelatedExtensibleModel):
 
         return stats
 
+    @property
+    def parent_groups_recursive(self) -> CTEQuerySet:
+        """Get all parent groups recursively."""
+
+        def _make_cte(cte):
+            Through = self.parent_groups.through
+            return (
+                Through.objects.values("to_group_id")
+                .filter(from_group=self)
+                .union(cte.join(Through, from_group=cte.col.to_group_id), all=True)
+            )
+
+        cte = With.recursive(_make_cte)
+        return cte.join(Group, id=cte.col.to_group_id).with_cte(cte)
+
+    @property
+    def child_groups_recursive(self) -> CTEQuerySet:
+        """Get all child groups recursively."""
+
+        def _make_cte(cte):
+            Through = self.child_groups.through
+            return (
+                Through.objects.values("from_group_id")
+                .filter(to_group=self)
+                .union(cte.join(Through, to_group=cte.col.from_group_id), all=True)
+            )
+
+        cte = With.recursive(_make_cte)
+        return cte.join(Group, id=cte.col.from_group_id).with_cte(cte)
+
+    @property
+    def members_recursive(self) -> QuerySet:
+        """Get all members of this group and its child groups."""
+        return Person.objects.filter(
+            Q(member_of=self) | Q(member_of__in=self.child_groups_recursive)
+        )
+
+    @property
+    def owners_recursive(self) -> QuerySet:
+        """Get all ownerss of this group and its parent groups."""
+        return Person.objects.filter(
+            Q(owner_of=self) | Q(owner_of__in=self.parent_groups_recursive)
+        )
+
     def __str__(self) -> str:
         if self.school_term:
             return f"{self.name} ({self.short_name}) ({self.school_term})"
@@ -1101,6 +1163,22 @@ class DataCheckResult(ExtensibleModel):
         )
 
 
+class PersonInvitation(AbstractBaseInvitation, PureDjangoModel):
+    """Custom model for invitations to allow to generate invitations codes without email address."""
+
+    email = models.EmailField(verbose_name=_("E-Mail address"), blank=True)
+    person = models.ForeignKey(
+        Person, on_delete=models.CASCADE, blank=True, related_name="invitation", null=True
+    )
+
+    def __str__(self) -> str:
+        return f"{self.email} ({self.inviter})"
+
+    key_expired = Invitation.key_expired
+
+    send_invitation = Invitation.send_invitation
+
+
 class PDFFile(ExtensibleModel):
     """Link to a rendered PDF file."""
 
@@ -1152,12 +1230,52 @@ class TaskUserAssignment(ExtensibleModel):
         verbose_name_plural = _("Task user assignments")
 
 
+class UserAdditionalAttributes(models.Model, PureDjangoModel):
+    """Additional attributes for Django user accounts.
+
+    These attributes are explicitly linked to a User, not to a Person.
+    """
+
+    user = models.OneToOneField(
+        get_user_model(),
+        on_delete=models.CASCADE,
+        related_name="additional_attributes",
+        verbose_name=_("Linked user"),
+    )
+
+    attributes = models.JSONField(verbose_name=_("Additional attributes"), default=dict)
+
+    @classmethod
+    def get_user_attribute(
+        cls, username: str, attribute: str, default: Optional[Any] = None
+    ) -> Any:
+        """Get a user attribute for a user by name."""
+        try:
+            attributes = cls.objects.get(user__username=username)
+        except cls.DoesNotExist:
+            return default
+
+        return attributes.attributes.get(attribute, default)
+
+    @classmethod
+    def set_user_attribute(cls, username: str, attribute: str, value: Any):
+        """Set a user attribute for a user by name.
+
+        Raises DoesNotExist if a username for a non-existing Django user is passed.
+        """
+        user = get_user_model().objects.get(username=username)
+        attributes, __ = cls.objects.update_or_create(user=user)
+
+        attributes.attributes[attribute] = value
+        attributes.save()
+
+
 class OAuthApplication(AbstractApplication):
     """Modified OAuth application class that supports Grant Flows configured in preferences."""
 
     # Override grant types to make field optional
     authorization_grant_type = models.CharField(
-        max_length=32, choices=AbstractApplication.GRANT_TYPES, blank=True, null=True
+        max_length=32, choices=AbstractApplication.GRANT_TYPES, blank=True
     )
 
     # Optional list of alloewd scopes
@@ -1168,6 +1286,15 @@ class OAuthApplication(AbstractApplication):
         blank=True,
     )
 
+    icon = models.ImageField(
+        verbose_name=_("Icon"),
+        blank=True,
+        null=True,
+        help_text=_(
+            "This image will be shown as icon in the authorization flow. It should be squared."
+        ),
+    )
+
     def allows_grant_type(self, *grant_types: set[str]) -> bool:
         allowed_grants = get_site_preferences()["auth__oauth_allowed_grants"]
 
diff --git a/aleksis/core/preferences.py b/aleksis/core/preferences.py
index d0de22fe03cc044fa5722982dfcc7faf86a4503c..cb351100a5da31c4d5cffc2255542bc86f4a77c1 100644
--- a/aleksis/core/preferences.py
+++ b/aleksis/core/preferences.py
@@ -3,6 +3,7 @@ from django.forms import EmailField, ImageField, URLField
 from django.forms.widgets import SelectMultiple
 from django.utils.translation import gettext_lazy as _
 
+import pycountry
 from colorfield.widgets import ColorWidget
 from dynamic_preferences.preferences import Section
 from dynamic_preferences.types import (
@@ -16,6 +17,7 @@ from dynamic_preferences.types import (
 )
 from oauth2_provider.models import AbstractApplication
 
+from .mixins import PublicFilePreferenceMixin
 from .models import Group, Person
 from .registries import person_preferences_registry, site_preferences_registry
 from .util.notifications import get_notification_choices_lazy
@@ -78,7 +80,7 @@ class ColourSecondary(StringPreference):
 
 
 @site_preferences_registry.register
-class Logo(FilePreference):
+class Logo(PublicFilePreferenceMixin, FilePreference):
     """Logo of your AlekSIS instance."""
 
     section = theme
@@ -88,7 +90,7 @@ class Logo(FilePreference):
 
 
 @site_preferences_registry.register
-class Favicon(FilePreference):
+class Favicon(PublicFilePreferenceMixin, FilePreference):
     """Favicon of your AlekSIS instance."""
 
     section = theme
@@ -98,7 +100,7 @@ class Favicon(FilePreference):
 
 
 @site_preferences_registry.register
-class PWAIcon(FilePreference):
+class PWAIcon(PublicFilePreferenceMixin, FilePreference):
     """PWA-Icon of your AlekSIS instance."""
 
     section = theme
@@ -263,6 +265,30 @@ class SignupEnabled(BooleanPreference):
     verbose_name = _("Enable signup")
 
 
+@site_preferences_registry.register
+class InviteEnabled(BooleanPreference):
+    section = auth
+    name = "invite_enabled"
+    default = False
+    verbose_name = _("Enable invitations")
+
+
+@site_preferences_registry.register
+class InviteCodeLength(IntegerPreference):
+    section = auth
+    name = "invite_code_length"
+    default = 3
+    verbose_name = _("Length of invite code. (Default 3: abcde-acbde-abcde)")
+
+
+@site_preferences_registry.register
+class InviteCodePacketSize(IntegerPreference):
+    section = auth
+    name = "invite_code_packet_size"
+    default = 5
+    verbose_name = _("Size of packets. (Default 5: abcde)")
+
+
 @site_preferences_registry.register
 class OAuthAllowedGrants(MultipleChoicePreference):
     """Grant Flows allowed for OAuth applications."""
@@ -406,3 +432,13 @@ class AutoUpdatingDashboardSite(BooleanPreference):
     name = "automatically_update_dashboard_site"
     default = True
     verbose_name = _("Automatically update the dashboard and its widgets sitewide")
+
+
+@site_preferences_registry.register
+class PhoneNumberCountry(ChoicePreference):
+    section = internationalisation
+    name = "phone_number_country"
+    required = True
+    default = "GB"
+    choices = [(x.alpha_2, x.alpha_2) for x in pycountry.countries]
+    verbose_name = _("Country for phone number parsing")
diff --git a/aleksis/core/settings.py b/aleksis/core/settings.py
index a72efec88014768c0c5a1243e8d9acc2f992d699..8e1207d490b839bbbdc9a7bda328451724779a83 100644
--- a/aleksis/core/settings.py
+++ b/aleksis/core/settings.py
@@ -1,4 +1,5 @@
 import os
+import warnings
 from glob import glob
 from socket import getfqdn
 
@@ -15,6 +16,7 @@ IN_PYTEST = "PYTEST_CURRENT_TEST" in os.environ or "TOX_ENV_DIR" in os.environ
 
 ENVVAR_PREFIX_FOR_DYNACONF = "ALEKSIS"
 DIRS_FOR_DYNACONF = ["/etc/aleksis"]
+MERGE_ENABLED_FOR_DYNACONF = True
 
 SETTINGS_FILE_FOR_DYNACONF = []
 for directory in DIRS_FOR_DYNACONF:
@@ -30,6 +32,7 @@ for directory in DIRS_FOR_DYNACONF:
 _settings = LazySettings(
     ENVVAR_PREFIX_FOR_DYNACONF=ENVVAR_PREFIX_FOR_DYNACONF,
     SETTINGS_FILE_FOR_DYNACONF=SETTINGS_FILE_FOR_DYNACONF,
+    MERGE_ENABLED_FOR_DYNACONF=MERGE_ENABLED_FOR_DYNACONF,
 )
 
 # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
@@ -94,6 +97,7 @@ INSTALLED_APPS = [
     "rules.apps.AutodiscoverRulesConfig",
     "haystack",
     "polymorphic",
+    "dj_cleavejs.apps.DjCleaveJSConfig",
     "dbbackup",
     "django_celery_beat",
     "django_celery_results",
@@ -122,6 +126,7 @@ INSTALLED_APPS = [
     "allauth",
     "allauth.account",
     "allauth.socialaccount",
+    "invitations",
     "health_check",
     "health_check.db",
     "health_check.cache",
@@ -199,6 +204,14 @@ TEMPLATES = [
     },
 ]
 
+# Attention: The following context processors must accept None
+# as first argument (in addition to a HttpRequest object)
+PDF_CONTEXT_PROCESSORS = [
+    "django.template.context_processors.i18n",
+    "django.template.context_processors.tz",
+    "aleksis.core.util.core_helpers.custom_information_processor",
+]
+
 WSGI_APPLICATION = "aleksis.core.wsgi.application"
 
 # Database
@@ -213,6 +226,7 @@ DATABASES = {
         "HOST": _settings.get("database.host", "127.0.0.1"),
         "PORT": _settings.get("database.port", "5432"),
         "CONN_MAX_AGE": _settings.get("database.conn_max_age", None),
+        "OPTIONS": _settings.get("database.options", {}),
     }
 }
 
@@ -320,8 +334,11 @@ ACCOUNT_AUTHENTICATION_METHOD = _settings.get("auth.registration.method", "usern
 ACCOUNT_EMAIL_REQUIRED = _settings.get("auth.registration.email_required", True)
 SOCIALACCOUNT_EMAIL_REQUIRED = False
 
-# Require email verification after sigm up
-ACCOUNT_EMAIL_VERIFICATION = _settings.get("auth.registration.email_verification", "mandatory")
+# Cooldown for verification mails
+ACCOUNT_EMAIL_CONFIRMATION_COOLDOWN = _settings.get("auth.registration.verification_cooldown", 180)
+
+# Require email verification after sign up
+ACCOUNT_EMAIL_VERIFICATION = _settings.get("auth.registration.email_verification", "optional")
 SOCIALACCOUNT_EMAIL_VERIFICATION = False
 
 # Email subject prefix for verification mails
@@ -339,26 +356,49 @@ ACCOUNT_SIGNUP_EMAIL_ENTER_TWICE = True
 # Enforce uniqueness of email addresses
 ACCOUNT_UNIQUE_EMAIL = _settings.get("auth.login.registration.unique_email", True)
 
+# Configuration for django-invitations
+
+# Use custom account adapter
+ACCOUNT_ADAPTER = "invitations.models.InvitationsAdapter"
+# Expire invitations are configured amout of days
+INVITATIONS_INVITATION_EXPIRY = _settings.get("auth.invitation.expiry", 3)
+# Use email prefix configured for django-allauth
+INVITATIONS_EMAIL_SUBJECT_PREFIX = ACCOUNT_EMAIL_SUBJECT_PREFIX
+# Use custom invitation model
+INVITATIONS_INVITATION_MODEL = "core.PersonInvitation"
+# Display error message if invitation code is invalid
+INVITATIONS_GONE_ON_ACCEPT_ERROR = False
+# Mark invitation as accepted after signup
+INVITATIONS_ACCEPT_INVITE_AFTER_SIGNUP = True
+
 # Configuration for OAuth2 provider
-OAUTH2_PROVIDER = {"SCOPES_BACKEND_CLASS": "aleksis.core.util.auth_helpers.AppScopes"}
+OAUTH2_PROVIDER = {
+    "SCOPES_BACKEND_CLASS": "aleksis.core.util.auth_helpers.AppScopes",
+    "OAUTH2_VALIDATOR_CLASS": "aleksis.core.util.auth_helpers.CustomOAuth2Validator",
+    "OIDC_ENABLED": True,
+}
 OAUTH2_PROVIDER_APPLICATION_MODEL = "core.OAuthApplication"
 OAUTH2_PROVIDER_GRANT_MODEL = "core.OAuthGrant"
 OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = "core.OAuthAccessToken"  # noqa: S105
 OAUTH2_PROVIDER_ID_TOKEN_MODEL = "core.OAuthIDToken"  # noqa: S105
 OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = "core.OAuthRefreshToken"  # noqa: S105
 
-if _settings.get("oauth2.oidc.enabled", False):
-    with open(_settings.get("oauth2.oidc.rsa_key", "/etc/aleksis/oidc.pem"), "r") as f:
-        oid_rsa_key = f.read()
-
-    OAUTH2_PROVIDER.update(
-        {
-            "OAUTH2_VALIDATOR_CLASS": "aleksis.core.util.auth_helpers.CustomOAuth2Validator",
-            "OIDC_ENABLED": True,
-            "OIDC_RSA_PRIVATE_KEY": oid_rsa_key,
-            #        "OIDC_ISS_ENDPOINT": _settings.get("oauth2.oidc.issuer_name", "example.com"),
-        }
+_OIDC_RSA_KEY_DEFAULT = "/etc/aleksis/oidc.pem"
+_OIDC_RSA_KEY = _settings.get("oauth2.oidc.rsa_key", "/etc/aleksis/oidc.pem")
+if "BEGIN RSA PRIVATE KEY" in _OIDC_RSA_KEY:
+    OAUTH2_PROVIDER["OIDC_RSA_PRIVATE_KEY"] = _OIDC_RSA_KEY
+elif _OIDC_RSA_KEY == _OIDC_RSA_KEY_DEFAULT and not os.path.exists(_OIDC_RSA_KEY):
+    warnings.warn(
+        (
+            f"The default OIDC RSA key in {_OIDC_RSA_KEY} does not exist. "
+            f"RSA will be disabled for now, but creating and configuring a "
+            f"key is recommended. To silence this warning, set oauth2.oidc.rsa_key "
+            f"to the empty string in a configuration file."
+        )
     )
+elif _OIDC_RSA_KEY:
+    with open(_OIDC_RSA_KEY, "r") as f:
+        OAUTH2_PROVIDER["OIDC_RSA_PRIVATE_KEY"] = f.read()
 
 # Configuration for REST framework
 REST_FRAMEWORK = {
@@ -379,6 +419,10 @@ if _settings.get("ldap.uri", None):
         PosixGroupType,
     )
 
+    AUTH_LDAP_GLOBAL_OPTIONS = {
+        ldap.OPT_NETWORK_TIMEOUT: _settings.get("ldap.network_timeout", 3),
+    }
+
     # Enable Django's integration to LDAP
     AUTHENTICATION_BACKENDS.append("aleksis.core.util.ldap.LDAPBackend")
 
@@ -389,7 +433,7 @@ if _settings.get("ldap.uri", None):
         AUTH_LDAP_BIND_DN = _settings.get("ldap.bind.dn")
         AUTH_LDAP_BIND_PASSWORD = _settings.get("ldap.bind.password")
 
-    # Keep local password for users to be required to proveide their old password on change
+    # Keep local password for users to be required to provide their old password on change
     AUTH_LDAP_SET_USABLE_PASSWORD = _settings.get("ldap.handle_passwords", True)
 
     # Keep bound as the authenticating user
@@ -463,7 +507,7 @@ if _settings.get("ldap.uri", None):
                 "is_superuser"
             ]
 
-# Add ModelBckend last so all other backends get a chance
+# Add ModelBackend last so all other backends get a chance
 # to verify passwords first
 AUTHENTICATION_BACKENDS.append("django.contrib.auth.backends.ModelBackend")
 
@@ -498,6 +542,7 @@ MEDIA_ROOT = _settings.get("media.root", os.path.join(BASE_DIR, "media"))
 NODE_MODULES_ROOT = _settings.get("node_modules.root", os.path.join(BASE_DIR, "node_modules"))
 
 YARN_INSTALLED_APPS = [
+    "cleave.js",
     "@fontsource/roboto",
     "jquery",
     "@materializecss/materialize",
@@ -539,10 +584,13 @@ ANY_JS = {
     "Roboto700": {"css_url": JS_URL + "/@fontsource/roboto/700.css"},
     "Roboto900": {"css_url": JS_URL + "/@fontsource/roboto/900.css"},
     "Sentry": {"js_url": JS_URL + "/@sentry/tracing/build/bundle.tracing.js"},
+    "cleavejs": {"js_url": "cleave.js/dist/cleave.min.js"},
 }
 
 merge_app_settings("ANY_JS", ANY_JS, True)
 
+CLEAVE_JS = ANY_JS["cleavejs"]["js_url"]
+
 SASS_PROCESSOR_ENABLED = True
 SASS_PROCESSOR_AUTO_INCLUDE = False
 SASS_PROCESSOR_CUSTOM_FUNCTIONS = {
@@ -658,7 +706,7 @@ if _settings.get("dev.uwsgi.celery", DEBUG):
 
 DEFAULT_FAVICON_PATHS = {
     "pwa_icon": os.path.join(STATIC_ROOT, "img/aleksis-icon.png"),
-    "favicon": os.path.join(STATIC_ROOT, "img/aleksis-icon.png"),
+    "favicon": os.path.join(STATIC_ROOT, "img/aleksis-favicon.png"),
 }
 PWA_ICONS_CONFIG = {
     "android": [192, 512],
@@ -857,10 +905,16 @@ PROMETHEUS_METRICS_EXPORT_ADDRESS = _settings.get("prometheus.metrucs.address",
 
 SECURE_PROXY_SSL_HEADER = ("REQUEST_SCHEME", "https")
 
+FILE_UPLOAD_HANDLERS = [
+    "django.core.files.uploadhandler.MemoryFileUploadHandler",
+    "django.core.files.uploadhandler.TemporaryFileUploadHandler",
+]
+
 if _settings.get("storage.type", "").lower() == "s3":
     INSTALLED_APPS.append("storages")
 
     DEFAULT_FILE_STORAGE = "storages.backends.s3boto3.S3Boto3Storage"
+    FILE_UPLOAD_HANDLERS.remove("django.core.files.uploadhandler.MemoryFileUploadHandler")
 
     if _settings.get("storage.s3.static.enabled", False):
         STATICFILES_STORAGE = "storages.backends.s3boto3.S3StaticStorage"
@@ -930,5 +984,13 @@ THUMBNAIL_PROCESSORS = (
 
 IMAGE_CROPPING_JQUERY_URL = None
 
+SHELL_PLUS_MODEL_IMPORTS_RESOLVER = "django_extensions.collision_resolvers.AppLabelPrefixCR"
+SHELL_PLUS_APP_PREFIXES = {
+    "auth": "auth",
+}
+SHELL_PLUS_DONT_LOAD = []
+merge_app_settings("SHELL_PLUS_APP_PREFIXES", SHELL_PLUS_APP_PREFIXES)
+merge_app_settings("SHELL_PLUS_DONT_LOAD", SHELL_PLUS_DONT_LOAD)
+
 # Add django-cleanup after all apps to ensure that it gets all signals as last app
 INSTALLED_APPS.append("django_cleanup.apps.CleanupConfig")
diff --git a/aleksis/core/static/icons/android_192.png b/aleksis/core/static/icons/android_192.png
deleted file mode 100644
index b5f7fec68184883830f68dabc606f7a95c45e8bb..0000000000000000000000000000000000000000
Binary files a/aleksis/core/static/icons/android_192.png and /dev/null differ
diff --git a/aleksis/core/static/icons/android_512.png b/aleksis/core/static/icons/android_512.png
deleted file mode 100644
index 5e042b4f98ef6dd8056ce643ad16ba085658684e..0000000000000000000000000000000000000000
Binary files a/aleksis/core/static/icons/android_512.png and /dev/null differ
diff --git a/aleksis/core/static/icons/apple_114.png b/aleksis/core/static/icons/apple_114.png
deleted file mode 100644
index eb8db13ad567105a819dab802b4f7ddd360e34be..0000000000000000000000000000000000000000
Binary files a/aleksis/core/static/icons/apple_114.png and /dev/null differ
diff --git a/aleksis/core/static/icons/apple_152.png b/aleksis/core/static/icons/apple_152.png
deleted file mode 100644
index 51eaa8d28988a16629e2b884d8de4df58fbadccc..0000000000000000000000000000000000000000
Binary files a/aleksis/core/static/icons/apple_152.png and /dev/null differ
diff --git a/aleksis/core/static/icons/apple_180.png b/aleksis/core/static/icons/apple_180.png
deleted file mode 100644
index 5c6b70846586ef0bf4bd44d177f6f79b49d1cddd..0000000000000000000000000000000000000000
Binary files a/aleksis/core/static/icons/apple_180.png and /dev/null differ
diff --git a/aleksis/core/static/icons/apple_76.png b/aleksis/core/static/icons/apple_76.png
deleted file mode 100644
index 3751b84654d01943148fc6f1b90125a4195d4af9..0000000000000000000000000000000000000000
Binary files a/aleksis/core/static/icons/apple_76.png and /dev/null differ
diff --git a/aleksis/core/static/icons/favicon_16.png b/aleksis/core/static/icons/favicon_16.png
deleted file mode 100644
index f1d02a7a8a34825726d20eeb1f6a63922d1a848e..0000000000000000000000000000000000000000
Binary files a/aleksis/core/static/icons/favicon_16.png and /dev/null differ
diff --git a/aleksis/core/static/icons/favicon_32.png b/aleksis/core/static/icons/favicon_32.png
deleted file mode 100644
index edc72d4556cce0fde923a0ac3db4d0f94bb74412..0000000000000000000000000000000000000000
Binary files a/aleksis/core/static/icons/favicon_32.png and /dev/null differ
diff --git a/aleksis/core/static/icons/favicon_48.png b/aleksis/core/static/img/aleksis-favicon.png
similarity index 100%
rename from aleksis/core/static/icons/favicon_48.png
rename to aleksis/core/static/img/aleksis-favicon.png
diff --git a/aleksis/core/static/js/main.js b/aleksis/core/static/js/main.js
index 1d2a2fe28e3c4304e72d8abfc398f0fda3908586..119103b5ea3885f290410a42758a5f672caffdc4 100644
--- a/aleksis/core/static/js/main.js
+++ b/aleksis/core/static/js/main.js
@@ -19,7 +19,8 @@ function initDatePicker(sel) {
 
         // Set monday as first day of week
         firstDay: get_format('FIRST_DAY_OF_WEEK'),
-        autoClose: true
+        autoClose: true,
+        yearRange: [new Date().getFullYear() - 100, new Date().getFullYear() + 100],
     });
     el.datepicker("setDate", $(sel).val());
     return el;
diff --git a/aleksis/core/static/js/progress.js b/aleksis/core/static/js/progress.js
index 9be718b7e67f30e8cc77a436755e50f08b540fd3..0b2509006029421273159617c9ff2646bb12800b 100644
--- a/aleksis/core/static/js/progress.js
+++ b/aleksis/core/static/js/progress.js
@@ -47,6 +47,7 @@ function customSuccess(progressBarElement, progressBarMessageElement) {
     $("#result-icon").text("check_circle");
     $("#result-text").text(OPTIONS.success);
     $("#result-box").show();
+    $("#result-button").show();
     const redirect = "redirect_on_success" in OPTIONS && OPTIONS.redirect_on_success;
     if (redirect) {
         window.location.replace(OPTIONS.redirect_on_success);
diff --git a/aleksis/core/static/public/style.scss b/aleksis/core/static/public/style.scss
index bef226c7d4a8583cb2e925453c9a4a27293cd3f5..4fb6d9b2815555082d18cfd7ef18025704010035 100644
--- a/aleksis/core/static/public/style.scss
+++ b/aleksis/core/static/public/style.scss
@@ -64,6 +64,10 @@ header, main, footer {
   margin-left: 300px;
 }
 
+.without-menu header, .without-menu main, .without-menu footer {
+  margin-left: 0;
+}
+
 @media only screen and (max-width: 992px) {
   header, main, footer {
     margin-left: 0;
@@ -377,6 +381,12 @@ span.badge .material-icons {
   margin-left: -2px;
 }
 
+.chip {
+  padding: 8px 12px;
+  height: auto;
+  line-height: 16px;
+}
+
 /*+++++++++*/
 /* Buttons */
 /*+++++++++*/
@@ -794,3 +804,15 @@ main figure.alert {
   height: 24px;
   margin-bottom: 8px;
 }
+
+.application-circle {
+  border-radius: 50%;
+  width: 20vh;
+  height: 20vh;
+}
+
+
+.application-circle img {
+  @extend .application-circle;
+    object-fit: cover;
+}
diff --git a/aleksis/core/tables.py b/aleksis/core/tables.py
index 8f0e0b93bf3b79b96a50a2566b450fda05426bc9..ab19c80608f1a8966dab7ae2094eca7caf579252 100644
--- a/aleksis/core/tables.py
+++ b/aleksis/core/tables.py
@@ -1,14 +1,19 @@
+from textwrap import wrap
+
 from django.utils.translation import gettext_lazy as _
 
 import django_tables2 as tables
 from django_tables2.utils import A
 
+from .models import Person
+from .util.core_helpers import get_site_preferences
+
 
 class SchoolTermTable(tables.Table):
     """Table to list persons."""
 
     class Meta:
-        attrs = {"class": "responsive-table highlight"}
+        attrs = {"class": "highlight"}
 
     name = tables.LinkColumn("edit_school_term", args=[A("id")])
     date_start = tables.Column()
@@ -26,7 +31,7 @@ class PersonsTable(tables.Table):
     """Table to list persons."""
 
     class Meta:
-        attrs = {"class": "responsive-table highlight"}
+        attrs = {"class": "highlight"}
 
     first_name = tables.LinkColumn("person_by_id", args=[A("id")])
     last_name = tables.LinkColumn("person_by_id", args=[A("id")])
@@ -36,7 +41,7 @@ class GroupsTable(tables.Table):
     """Table to list groups."""
 
     class Meta:
-        attrs = {"class": "responsive-table highlight"}
+        attrs = {"class": "highlight"}
 
     name = tables.LinkColumn("group_by_id", args=[A("id")])
     short_name = tables.LinkColumn("group_by_id", args=[A("id")])
@@ -47,7 +52,7 @@ class AdditionalFieldsTable(tables.Table):
     """Table to list group types."""
 
     class Meta:
-        attrs = {"class": "responsive-table hightlight"}
+        attrs = {"class": "hightlight"}
 
     title = tables.LinkColumn("edit_additional_field_by_id", args=[A("id")])
     delete = tables.LinkColumn(
@@ -63,7 +68,7 @@ class GroupTypesTable(tables.Table):
     """Table to list group types."""
 
     class Meta:
-        attrs = {"class": "responsive-table highlight"}
+        attrs = {"class": "highlight"}
 
     name = tables.LinkColumn("edit_group_type_by_id", args=[A("id")])
     description = tables.LinkColumn("edit_group_type_by_id", args=[A("id")])
@@ -76,7 +81,7 @@ class DashboardWidgetTable(tables.Table):
     """Table to list dashboard widgets."""
 
     class Meta:
-        attrs = {"class": "responsive-table highlight"}
+        attrs = {"class": "highlight"}
 
     widget_name = tables.Column(accessor="pk")
     title = tables.LinkColumn("edit_dashboard_widget", args=[A("id")])
@@ -93,6 +98,33 @@ class DashboardWidgetTable(tables.Table):
         return record._meta.verbose_name
 
 
+class PersonColumn(tables.Column):
+    """Returns person object from given id."""
+
+    def render(self, value):
+        return Person.objects.get(user__id=value)
+
+
+class InvitationCodeColumn(tables.Column):
+    """Returns invitation code in a more readable format."""
+
+    def render(self, value):
+        packet_size = get_site_preferences()["auth__invite_code_packet_size"]
+        return "-".join(wrap(value, packet_size))
+
+
+class InvitationsTable(tables.Table):
+    """Table to list persons."""
+
+    email = tables.EmailColumn()
+    sent = tables.DateColumn()
+    inviter_id = PersonColumn()
+    key = InvitationCodeColumn()
+    accepted = tables.BooleanColumn(
+        yesno="check,cancel", attrs={"span": {"class": "material-icons"}}
+    )
+
+
 class PermissionDeleteColumn(tables.LinkColumn):
     """Link column with label 'Delete'."""
 
diff --git a/aleksis/core/templates/404.html b/aleksis/core/templates/404.html
index 33c311fcaf4c106d44c53788b44426584d014c33..cf68c12bf63624ecf40107ee6f3994b288548c3c 100644
--- a/aleksis/core/templates/404.html
+++ b/aleksis/core/templates/404.html
@@ -7,8 +7,7 @@
     <div class="card red">
       <div class="card-content white-text">
         <i class="material-icons small left">error_outline</i>
-        <span class="card-title">{% trans "Error" %} (404): {% blocktrans %}The requested page or object was not
-          found.{% endblocktrans %}</span>
+        <span class="card-title">{{ exception }}</span>
         <p>
           {% blocktrans %}
             If you were redirected by a link on an external page,
diff --git a/aleksis/core/templates/account/account_inactive.html b/aleksis/core/templates/account/account_inactive.html
index 62b03a3db859699aabb816ef1d05455777965bd9..5c328abcfbca48c6499f71b4b11fac5f1f91ccad 100644
--- a/aleksis/core/templates/account/account_inactive.html
+++ b/aleksis/core/templates/account/account_inactive.html
@@ -9,8 +9,10 @@
   <div class="container">
     <div class="card red">
       <div class="card-content white-text">
-        <div class="material-icons small left">error_outline</div>
-        <span class="card-title">{% blocktrans %}Account inactive.{% endblocktrans %}</span>
+        <div class="card-title">
+          <i class="material-icons small left">error_outline</i>
+          {% blocktrans %}Account inactive.{% endblocktrans %}
+        </div>
         <p>
           {% blocktrans %}
             This account is currently inactive. If you think this is an
diff --git a/aleksis/core/templates/account/signup_closed.html b/aleksis/core/templates/account/signup_closed.html
index 33ef749f5598fbd209e3f72dc2b4a79620f17b1a..0d5cbd51095e9c24fceacbc4ba8f6a510a5705a9 100644
--- a/aleksis/core/templates/account/signup_closed.html
+++ b/aleksis/core/templates/account/signup_closed.html
@@ -9,8 +9,10 @@
   <div class="container">
     <div class="card red">
       <div class="card-content white-text">
-        <div class="material-icons small left">error_outline</div>
-        <span class="card-title">{% blocktrans %}Signup closed.{% endblocktrans %}</span>
+        <div class="card-title">
+        <i class="material-icons small left">error_outline</i>
+          {% blocktrans %}Signup closed.{% endblocktrans %}
+        </div>
         <p>
           {% blocktrans %}
             This sign up is currently closed. If you think this is an
diff --git a/aleksis/core/templates/account/verification_sent.html b/aleksis/core/templates/account/verification_sent.html
index dd486aeb2bf212838a014fe868db4cfe6716cf8d..df3ae47c82911a01997ac9f4d7919c9e1b95a36e 100644
--- a/aleksis/core/templates/account/verification_sent.html
+++ b/aleksis/core/templates/account/verification_sent.html
@@ -25,10 +25,6 @@
             contact us if you do not receive it within a few minutes.
           {% endblocktrans %}
         </p>
-        <p>
-          {% url 'account_email' as email_url %}
-          {% blocktrans with email_url=email_url %}<strong>Note:</strong> you can still <a href="{{ email_url }}">change your e-mail address</a>{% endblocktrans %}
-        </p>
         {% include "core/partials/admins_list.html" %}
       </div>
     </div>
diff --git a/aleksis/core/templates/core/base.html b/aleksis/core/templates/core/base.html
index 6c47c2a3bd0da7ba02e4c8aa96e7a5787cd933c0..7fd4e38f8449b0a649fcd01e08051a2332070a15 100644
--- a/aleksis/core/templates/core/base.html
+++ b/aleksis/core/templates/core/base.html
@@ -58,7 +58,7 @@
 
   {% block extra_head %}{% endblock %}
 </head>
-<body>
+<body {% if no_menu %}class="without-menu"{% endif %}>
 
 <header>
   <!-- Menu button (sidenav) -->
@@ -88,32 +88,36 @@
   </nav>
 
   <!-- Main nav (sidenav) -->
-  <ul id="slide-out" class="sidenav sidenav-fixed">
-    <li class="logo">
-      {% static "img/aleksis-banner.svg" as aleksis_banner %}
-      <a id="logo-container" href="/" class="brand-logo">
-        <img src="{% firstof request.site.preferences.theme__logo.url aleksis_banner %}"
-             alt="{{ request.site.preferences.general__title }} – Logo">
-      </a>
-    </li>
-    {% has_perm 'core.search_rule' user as search %}
-    {% if search %}
-      <li class="search">
-        <form method="get" action="{% url "haystack_search" %}" id="search-form" class="autocomplete">
-          <div class="search-wrapper">
-            <input id="search" name="q" type="search" enterkeyhint="search" placeholder="{% trans "Search" %}">
-            <button class="btn btn-flat search-button" type="submit" aria-label="{% trans "Search" %}">
-              <i class="material-icons">search</i>
-            </button>
-            <div class="progress" id="search-loader"><div class="indeterminate"></div></div>
-          </div>
-        </form>
+  {% if not no_menu %}
+    <ul id="slide-out" class="sidenav sidenav-fixed">
+      <li class="logo">
+        {% static "img/aleksis-banner.svg" as aleksis_banner %}
+        <a id="logo-container" href="/" class="brand-logo">
+          <img src="{% firstof request.site.preferences.theme__logo.url aleksis_banner %}"
+               alt="{{ request.site.preferences.general__title }} – Logo">
+        </a>
       </li>
-    {% endif %}
-    <li class="no-padding">
-      {% include "core/partials/sidenav.html" %}
-    </li>
-  </ul>
+      {% has_perm 'core.search_rule' user as search %}
+      {% if search %}
+        <li class="search">
+          <form method="get" action="{% url "haystack_search" %}" id="search-form" class="autocomplete">
+            <div class="search-wrapper">
+              <input id="search" name="q" type="search" enterkeyhint="search" placeholder="{% trans "Search" %}">
+              <button class="btn btn-flat search-button" type="submit" aria-label="{% trans "Search" %}">
+                <i class="material-icons">search</i>
+              </button>
+              <div class="progress" id="search-loader">
+                <div class="indeterminate"></div>
+              </div>
+            </div>
+          </form>
+        </li>
+      {% endif %}
+      <li class="no-padding">
+        {% include "core/partials/sidenav.html" %}
+      </li>
+    </ul>
+  {% endif %}
 </header>
 
 
@@ -180,7 +184,7 @@
         <span id="doit"></span>
         {% if request.site.preferences.footer__imprint_url %}
           <a class="blue-text text-lighten-4" href="{{ request.site.preferences.footer__imprint_url }}">
-            {% trans "Impress" %}
+            {% trans "Imprint" %}
           </a>
         {% endif %}
         {% if request.site.preferences.footer__privacy_url and request.site.preferences.footer__imprint_url %}
diff --git a/aleksis/core/templates/core/data_check/list.html b/aleksis/core/templates/core/data_check/list.html
index 5a510fdd92f550ad1b7ff6b0b81983ea225420ba..36d83d4cd7d3c917db98c104586bd2aa9c82c139 100644
--- a/aleksis/core/templates/core/data_check/list.html
+++ b/aleksis/core/templates/core/data_check/list.html
@@ -50,27 +50,29 @@
           </thead>
           <tbody>
           {% for result in results %}
-            <tr>
-              <td>
-                <code>{{ result.id }}</code>
-              </td>
-              <td>{% verbose_name_object result.related_object %}</td>
-              <td>{{ result.related_object }}</td>
-              <td>{{ result.related_check.problem_name }}</td>
-              <td>
-                <a class="btn-flat waves-effect waves-light" href="{{ result.related_object.get_absolute_url }}">
-                  {% trans "Show object" %}
-                </a>
-              </td>
-              <td>
-                {% for option_name, option in result.related_check.solve_options.items %}
-                  <a class="btn btn-margin waves-effect waves-light"
-                     href="{% url "data_check_solve" result.pk option_name %}">
-                    {{ option.verbose_name }}
+            {% if result.related_object %}
+              <tr>
+                <td>
+                  <code>{{ result.id }}</code>
+                </td>
+                <td>{% verbose_name_object result.related_object %}</td>
+                <td>{{ result.related_object }}</td>
+                <td>{{ result.related_check.problem_name }}</td>
+                <td>
+                  <a class="btn-flat waves-effect waves-light" href="{{ result.related_object.get_absolute_url }}">
+                    {% trans "Show object" %}
                   </a>
-                {% endfor %}
-              </td>
-            </tr>
+                </td>
+                <td>
+                  {% for option_name, option in result.related_check.solve_options.items %}
+                    <a class="btn btn-margin waves-effect waves-light"
+                       href="{% url "data_check_solve" result.pk option_name %}">
+                      {{ option.verbose_name }}
+                    </a>
+                  {% endfor %}
+                </td>
+              </tr>
+            {% endif %}
           {% endfor %}
           </tbody>
         </table>
diff --git a/aleksis/core/templates/core/pages/progress.html b/aleksis/core/templates/core/pages/progress.html
index 799f1f93648ab1de05be3f9432653b8c4e8b1466..fca1ddfc2ebe5980ee132de77bdf1baafce24ae0 100644
--- a/aleksis/core/templates/core/pages/progress.html
+++ b/aleksis/core/templates/core/pages/progress.html
@@ -47,7 +47,7 @@
           {% trans "Go back" %}
         </a>
         {% if additional_button %}
-          <a class="btn waves-effect waves-light" href="{{ additional_button.href }}">
+          <a class="btn waves-effect waves-light" href="{{ additional_button.href }}" id="result-button" style="display: none;">
             <i class="material-icons left">{{ additional_button.icon|default:"" }}</i>
             {{ additional_button.caption }}
           </a>
diff --git a/aleksis/core/templates/core/person/full.html b/aleksis/core/templates/core/person/full.html
index d16354f00ebcd2162ffc7bab16715707b41adb81..4a2e68a09517d1888f708b8ece3f757c6b1d8b1f 100644
--- a/aleksis/core/templates/core/person/full.html
+++ b/aleksis/core/templates/core/person/full.html
@@ -14,6 +14,7 @@
   {% has_perm 'core.change_person_preferences_rule' user person as can_change_person_preferences %}
   {% has_perm 'core.delete_person_rule' user person as can_delete_person %}
   {% has_perm "core.impersonate_rule" user person as can_impersonate %}
+  {% has_perm "core.can_invite" user person as can_invite %}
 
   {% if can_change_person or can_change_person_preferences or can_delete_person or can_impersonate %}
     <p>
@@ -43,7 +44,13 @@
           <i class="material-icons left">portrait</i>
           {% trans "Impersonate" %}
         </a>
-      {% endif %}
+    {% endif %}
+    {% if can_invite and not person.user %}
+        <a href="{% url "invite_person_by_id" person.id %}" class="btn waves-effect waves-light">
+          <i class="material-icons left">card_giftcard</i>
+          {% trans "Invite user" %}
+        </a>
+    {% endif %}
     </p>
   {% endif %}
 
diff --git a/aleksis/core/templates/dynamic_preferences/form.html b/aleksis/core/templates/dynamic_preferences/form.html
index 692d25e90b3dd5cb6554fe23010f5cbc542c3c94..22f036912c6493ac3f9a17815f1c46c6bdb413fe 100644
--- a/aleksis/core/templates/dynamic_preferences/form.html
+++ b/aleksis/core/templates/dynamic_preferences/form.html
@@ -10,7 +10,7 @@
   {% elif registry_name == "person" and instance == request.user.person %}
     {% blocktrans %}My preferences{% endblocktrans %}
   {% else %}
-    {% blocktrans with instace=instance %}Preferences for {{ instance }}{% endblocktrans %}
+    {% blocktrans with instance=instance %}Preferences for {{ instance }}{% endblocktrans %}
   {% endif %}
 {% endblock %}
 
diff --git a/aleksis/core/templates/impersonate/list_users.html b/aleksis/core/templates/impersonate/list_users.html
deleted file mode 100644
index fd273ca60902e5defbc41e567a9205cfe99fb349..0000000000000000000000000000000000000000
--- a/aleksis/core/templates/impersonate/list_users.html
+++ /dev/null
@@ -1,28 +0,0 @@
-{# -*- engine:django -*- #}
-{# Derived from the original template in django-impersonate #}
-
-{% extends "core/base.html" %}
-{% load i18n %}
-
-{% block browser_title %}{% trans "Impersonate" %}{% endblock %}
-{% block page_title %}{% trans "Impersonate" %}{% endblock %}
-
-{% block content %}
-  {% if page.object_list %}
-    <div class="collection">
-      {% for user in page.object_list %}
-        <a class="collection-item" href="{% url 'impersonate-start' user.pk %}{{ redirect }}">
-          {{ user }}
-        </a>
-      {% endfor %}
-    </div>
-  {% endif %}
-
-  {% if page.has_previous %}
-    <a href="{% url 'impersonate-list' %}?page={{ page.previous_page_number }}">Previous Page</a> &nbsp;
-  {% endif %}
-
-  {% if page.has_next %}
-    <a href="{% url 'impersonate-list' %}?page={{ page.next_page_number }}">Next Page</a> &nbsp;
-  {% endif %}
-{% endblock %}
diff --git a/aleksis/core/templates/invitations/enter.html b/aleksis/core/templates/invitations/enter.html
new file mode 100644
index 0000000000000000000000000000000000000000..03605a710697334567384a74a289783b64a7f55e
--- /dev/null
+++ b/aleksis/core/templates/invitations/enter.html
@@ -0,0 +1,44 @@
+{# -*- engine:django -*- #}
+
+{% extends "core/base.html" %}
+
+{% load material_form i18n %}
+
+{% block browser_title %}{% blocktrans %}Accept invitation{% endblocktrans %}{% endblock %}
+{% block no_page_title %}{% endblock %}
+
+{% block extra_head %}
+  {{ form.media.css }}
+{% endblock %}
+
+{% block content %}
+  <div class="row">
+  <div class="col m1 l2 xl3"></div>
+  <div class="col s12 m10 l8 xl6">
+    <div class="card">
+      <form method="post">
+        <div class="card-content">
+          <div class="card-title">{% trans "Accept your invitation" %}</div>
+          <div class="alert primary">
+            <div>
+              <i class="material-icons left">info</i>
+              {% blocktrans %}
+                Please enter your invitation code to register
+                your new user account:
+              {% endblocktrans %}
+            </div>
+          </div>
+          {% csrf_token %}
+          {% form form=form %}{% endform %}
+        </div>
+        <div class="card-action-light">
+          <button type="submit" class="btn green waves-effect waves-light">
+            <i class="material-icons left">card_giftcard</i>
+            {% trans "Accept invite" %}
+          </button>
+        </div>
+      </form>
+    </div>
+  </div>
+  {{ form.media.js }}
+{% endblock %}
diff --git a/aleksis/core/templates/invitations/forms/_invite.html b/aleksis/core/templates/invitations/forms/_invite.html
new file mode 100644
index 0000000000000000000000000000000000000000..297997e0b04f3d99a1e1279c8900d0bcc9c50024
--- /dev/null
+++ b/aleksis/core/templates/invitations/forms/_invite.html
@@ -0,0 +1,38 @@
+{# -*- engine:django -*- #}
+
+{% extends "core/base.html" %}
+
+{% load material_form i18n %}
+
+{% load render_table from django_tables2 %}
+
+{% block browser_title %}{% blocktrans %}Invite{% endblocktrans %}{% endblock %}
+{% block page_title %}{% blocktrans %}Invite{% endblocktrans %}{% endblock %}
+
+
+{% block content %}
+
+  <div class="row">
+    <div class="col s6">
+      <h5>{% trans "Invite by email address" %}</h5>
+      <form method="post">
+        {% csrf_token %}
+        {% form form=form %}{% endform %}
+        {% trans "Invite" as caption %}
+        {% include "core/partials/save_button.html" with caption=caption icon="card_giftcard" %}
+      </form>
+    </div>
+    <div class="col s6">        
+      <h5>{% trans "Generate invitation code" %}</h5>
+
+      <a class="btn waves-effect waves-light hundred-percent" href="{% url 'generate_invitation_code' %}">
+        {% trans "Generate code" %}
+      </a>
+    </div>
+    <div class="col s12">
+      <h5>{% trans "Invitations" %}</h5>
+
+      {% render_table table %}
+    </div>
+  </div>
+{% endblock %}
diff --git a/aleksis/core/templates/invitations/messages/invite_accepted.txt b/aleksis/core/templates/invitations/messages/invite_accepted.txt
new file mode 100644
index 0000000000000000000000000000000000000000..59699adee30108f241eb6dd4db1478f75399292d
--- /dev/null
+++ b/aleksis/core/templates/invitations/messages/invite_accepted.txt
@@ -0,0 +1,3 @@
+{% load i18n %}
+
+{% blocktrans %}The invitation for {{ email }} has been accepted.{% endblocktrans %}
diff --git a/aleksis/core/templates/oauth2_provider/application/create.html b/aleksis/core/templates/oauth2_provider/application/create.html
index d81489e922a76de8d7a8e92a7f48686714a3b3a7..73b94677206d38fca163858551536d575b2dce3d 100644
--- a/aleksis/core/templates/oauth2_provider/application/create.html
+++ b/aleksis/core/templates/oauth2_provider/application/create.html
@@ -6,7 +6,7 @@
 {% block page_title %}{% blocktrans %}Register OAuth2 Application{% endblocktrans %}{% endblock %}
 
 {% block content %}
-  <form method="post">
+  <form method="post" enctype="multipart/form-data">
     {% csrf_token %}
     {% form form=form %}{% endform %}
     {% include "core/partials/save_button.html" %}
diff --git a/aleksis/core/templates/oauth2_provider/application/detail.html b/aleksis/core/templates/oauth2_provider/application/detail.html
index da6b8abf81ddc1916de7b0c7feb5a7262f453037..28e2af7d70ff4b6dd5d93b46b8ec52d31bc36538 100644
--- a/aleksis/core/templates/oauth2_provider/application/detail.html
+++ b/aleksis/core/templates/oauth2_provider/application/detail.html
@@ -22,6 +22,18 @@
   </a>
   <table class="responsive-table">
     <tbody>
+    <tr>
+      <th>{% trans "Icon" %}</th>
+      <td>
+        {% if application.icon %}
+          <div class="application-circle materialboxed z-depth-2">
+            <img src="{{ application.icon.url }}" alt="{{ oauth_application.name }}" class="hundred-percent">
+          </div>
+        {% else %}
+          –
+        {% endif %}
+      </td>
+    </tr>
     <tr>
       <th>
         {% trans "Client id" %}
diff --git a/aleksis/core/templates/oauth2_provider/application/edit.html b/aleksis/core/templates/oauth2_provider/application/edit.html
index 6755d2420fb6a181f671b825475afb4ec9581521..30f50fff94e330e941d7b4730fe7d875b039d74e 100644
--- a/aleksis/core/templates/oauth2_provider/application/edit.html
+++ b/aleksis/core/templates/oauth2_provider/application/edit.html
@@ -6,7 +6,7 @@
 {% block page_title %}{% blocktrans %}Edit OAuth2 Application{% endblocktrans %}{% endblock %}
 
 {% block content %}
-  <form method="post">
+  <form method="post" enctype="multipart/form-data">
     {% csrf_token %}
     {% form form=form %}{% endform %}
     {% include "core/partials/save_button.html" %}
diff --git a/aleksis/core/templates/oauth2_provider/application/list.html b/aleksis/core/templates/oauth2_provider/application/list.html
index 06f1a95c4e05c14dcb5efe69f2416040d184f0bb..ced7d718dfe1b561c2ea8253e25658a9f449024d 100644
--- a/aleksis/core/templates/oauth2_provider/application/list.html
+++ b/aleksis/core/templates/oauth2_provider/application/list.html
@@ -12,8 +12,13 @@
   </a>
   <div class="collection">
     {% for application in applications %}
-      <a class="collection-item" href="{% url "oauth2_application" application.id %}">
-        {{ application.name }}
+      <a class="collection-item avatar" href="{% url "oauth2_application" application.id %}">
+        {% if application.icon %}
+          <img src="{{ application.icon.url }}" alt="{{ application.name }}" class="circle">
+        {% endif %}
+        <span class="title">
+          {{ application.name }}
+        </span>
       </a>
       {% empty %}
       <div class="collection-item flow-text">
diff --git a/aleksis/core/templates/oauth2_provider/authorize.html b/aleksis/core/templates/oauth2_provider/authorize.html
index 48b996837b8721ef6ba74337d286236e91729f5b..c90d5e8dd9d3d72cff9e19ce167487900e904636 100644
--- a/aleksis/core/templates/oauth2_provider/authorize.html
+++ b/aleksis/core/templates/oauth2_provider/authorize.html
@@ -12,8 +12,15 @@
     <div class="col s12 m10 l8 xl6">
       <div class="card">
         <div class="card-content">
-          <div class="card-title">
-            {% trans "Authorize" %} {{ application.name }}
+          {% if application.icon %}
+            <div class="center-via-flex margin-bottom">
+              <div class="application-circle materialboxed z-depth-2">
+                <img src="{{ application.icon.url }}" alt="{{ application.name }}" class="hundred-percent">
+              </div>
+            </div>
+          {% endif %}
+          <div class="card-title {% if application.icon %}center{% endif %}">
+            {% blocktrans with name=application.name %}Authorize {{ name }}{% endblocktrans %}
           </div>
           <p class="margin-bottom">{% trans "The application requests access to the following scopes:" %}</p>
           {% for scope in scopes_descriptions %}
diff --git a/aleksis/core/templates/socialaccount/login.html b/aleksis/core/templates/socialaccount/login.html
new file mode 100644
index 0000000000000000000000000000000000000000..1630b01fa4104f563a92663bc7fd386a2abd5266
--- /dev/null
+++ b/aleksis/core/templates/socialaccount/login.html
@@ -0,0 +1,33 @@
+{% extends "core/base.html" %}
+
+{% load i18n material_form account %}
+
+{% block browser_title %}{% trans "Authorize" %}{% endblock %}
+{% block page_title %}{% trans "Authorize" %}{% endblock %}
+
+{% block content %}
+{% if process == "connect" %}
+    <p class="flow-text">
+       <i class="material-icons left">info</i>
+       {% blocktrans with provider.name as provider %}You are about to connect a new third party account from {{ provider }}.{% endblocktrans %}
+    </p>
+    <form method="post">
+      {% csrf_token %}
+      {% form form=form %}{% endform %}
+      {% trans "Confirm" as caption %}
+      {% include "core/partials/save_button.html" with caption=caption icon="how_to_reg" %}
+    </form>
+{% else %}
+    <p class="flow-text">
+       <i class="material-icons left small">info</i>
+       {% blocktrans with provider.name as provider %}You are about to sign in using a third party account from {{ provider }}.{% endblocktrans %}
+    </p>
+    <form method="post">
+      {% csrf_token %}
+      {% form form=form %}{% endform %}
+      {% trans "Continue" as caption %}
+      {% include "core/partials/save_button.html" with caption=caption icon="how_to_reg" %}
+    </form>
+
+{% endif %}
+{% endblock %}
diff --git a/aleksis/core/templates/two_factor/core/login.html b/aleksis/core/templates/two_factor/core/login.html
index 9ea4bc6c24b7d9e764742bbddf75ceb86b785868..834c4b98b543bd994542b105aec1b99932f7d186 100644
--- a/aleksis/core/templates/two_factor/core/login.html
+++ b/aleksis/core/templates/two_factor/core/login.html
@@ -16,7 +16,17 @@
       <div class="col s12 m10 l8 xl6">
         <div class="card">
           <div class="card-content">
-            {% if wizard.steps.current == 'auth' and socialaccount_providers %}
+            {% if oauth and oauth_application.icon %}
+              <div class="center-via-flex margin-bottom">
+                <div class="application-circle materialboxed z-depth-2">
+                  <img src="{{ oauth_application.icon.url }}" alt="{{ oauth_application.name }}"
+                       class="hundred-percent">
+                </div>
+              </div>
+              <div class="card-title center">
+                {% blocktrans with name=oauth_application.name %}Login for {{ name }}{% endblocktrans %}
+              </div>
+            {% elif wizard.steps.current == 'auth' and socialaccount_providers %}
               <div class="card-title">{% trans "Login with username and password" %}</div>
             {% else %}
               <div class="card-title">{% trans "Login" %}</div>
@@ -30,12 +40,21 @@
                 </p>
               </div>
             {% elif wizard.steps.current == 'auth' %}
-              <div class="alert primary">
-                <p>
-                  <i class="material-icons left">info</i>
-                  {% blocktrans %}Please login to see this page.{% endblocktrans %}
-                </p>
-              </div>
+              {% if oauth %}
+                <div class="alert primary">
+                  <p>
+                    <i class="material-icons left">info</i>
+                    {% blocktrans %}Please login with your account to use the external application.{% endblocktrans %}
+                  </p>
+                </div>
+              {% else %}
+                <div class="alert primary">
+                  <p>
+                    <i class="material-icons left">info</i>
+                    {% blocktrans %}Please login to see this page.{% endblocktrans %}
+                  </p>
+                </div>
+              {% endif %}
             {% endif %}
             {% if not wizard.steps.current == "auth" %}
               <div class="alert primary">
diff --git a/aleksis/core/tests/models/test_group.py b/aleksis/core/tests/models/test_group.py
new file mode 100644
index 0000000000000000000000000000000000000000..8849a842d043b6bd3c91d04393cf5ccc695d2cf3
--- /dev/null
+++ b/aleksis/core/tests/models/test_group.py
@@ -0,0 +1,163 @@
+import pytest
+
+from aleksis.core.models import Group, Person
+
+pytestmark = pytest.mark.django_db
+
+
+def test_child_groups_recursive():
+    g_1st_grade = Group.objects.create(name="1st grade")
+    g_1a = Group.objects.create(name="1a")
+    g_1b = Group.objects.create(name="1b")
+    g_2nd_grade = Group.objects.create(name="2nd grade")
+    g_2a = Group.objects.create(name="2a")
+    g_2b = Group.objects.create(name="2b")
+    g_2c = Group.objects.create(name="2c")
+    g_2nd_grade_french = Group.objects.create(name="2nd grade French")
+
+    g_1a.parent_groups.set([g_1st_grade])
+    g_1b.parent_groups.set([g_1st_grade])
+    g_2a.parent_groups.set([g_2nd_grade])
+    g_2b.parent_groups.set([g_2nd_grade])
+    g_2c.parent_groups.set([g_2nd_grade])
+    g_2nd_grade_french.parent_groups.set([g_2b, g_2c])
+
+    assert g_2nd_grade_french in g_2nd_grade.child_groups_recursive
+    assert g_2nd_grade_french in g_2b.child_groups_recursive
+    assert g_2nd_grade_french in g_2c.child_groups_recursive
+    assert g_2nd_grade_french not in g_2a.child_groups_recursive
+    assert g_2nd_grade_french not in g_1st_grade.child_groups_recursive
+
+
+def test_parent_groups_recursive():
+    g_1st_grade = Group.objects.create(name="1st grade")
+    g_1a = Group.objects.create(name="1a")
+    g_1b = Group.objects.create(name="1b")
+    g_2nd_grade = Group.objects.create(name="2nd grade")
+    g_2a = Group.objects.create(name="2a")
+    g_2b = Group.objects.create(name="2b")
+    g_2c = Group.objects.create(name="2c")
+    g_2nd_grade_french = Group.objects.create(name="2nd grade French")
+
+    g_1a.parent_groups.set([g_1st_grade])
+    g_1b.parent_groups.set([g_1st_grade])
+    g_2a.parent_groups.set([g_2nd_grade])
+    g_2b.parent_groups.set([g_2nd_grade])
+    g_2c.parent_groups.set([g_2nd_grade])
+    g_2nd_grade_french.parent_groups.set([g_2b, g_2c])
+
+    assert g_1st_grade in g_1a.parent_groups_recursive
+    assert g_2nd_grade in g_2a.parent_groups_recursive
+    assert g_2nd_grade in g_2nd_grade_french.parent_groups_recursive
+    assert g_1st_grade not in g_2nd_grade_french.parent_groups_recursive
+
+
+def test_members_recursive():
+    g_2nd_grade = Group.objects.create(name="2nd grade")
+    g_2a = Group.objects.create(name="2a")
+    g_2b = Group.objects.create(name="2b")
+    g_2c = Group.objects.create(name="2c")
+    g_2nd_grade_french = Group.objects.create(name="2nd grade French")
+
+    g_2a.parent_groups.set([g_2nd_grade])
+    g_2b.parent_groups.set([g_2nd_grade])
+    g_2c.parent_groups.set([g_2nd_grade])
+    g_2nd_grade_french.parent_groups.set([g_2b, g_2c])
+
+    p_2a_1 = Person.objects.create(first_name="A", last_name="B")
+    p_2a_2 = Person.objects.create(first_name="A", last_name="B")
+    p_2b_1 = Person.objects.create(first_name="A", last_name="B")
+    p_2b_2 = Person.objects.create(first_name="A", last_name="B")
+    p_2c_1 = Person.objects.create(first_name="A", last_name="B")
+    p_2c_2 = Person.objects.create(first_name="A", last_name="B")
+    p_french_only = Person.objects.create(first_name="A", last_name="B")
+
+    g_2a.members.set([p_2a_1, p_2a_2])
+    g_2b.members.set([p_2b_1, p_2b_2])
+    g_2c.members.set([p_2c_1, p_2c_2])
+    g_2nd_grade_french.members.set([p_2b_1, p_2c_1, p_french_only])
+
+    assert p_2a_1 in g_2nd_grade.members_recursive
+    assert p_2a_2 in g_2nd_grade.members_recursive
+    assert p_2b_1 in g_2nd_grade.members_recursive
+    assert p_2b_2 in g_2nd_grade.members_recursive
+    assert p_2c_1 in g_2nd_grade.members_recursive
+    assert p_2c_2 in g_2nd_grade.members_recursive
+    assert p_french_only in g_2nd_grade.members_recursive
+    assert p_french_only in g_2b.members_recursive
+    assert p_french_only in g_2c.members_recursive
+    assert p_french_only not in g_2a.members_recursive
+
+
+def test_member_of_recursive():
+    g_2nd_grade = Group.objects.create(name="2nd grade")
+    g_2a = Group.objects.create(name="2a")
+    g_2b = Group.objects.create(name="2b")
+    g_2c = Group.objects.create(name="2c")
+    g_2nd_grade_french = Group.objects.create(name="2nd grade French")
+
+    g_2a.parent_groups.set([g_2nd_grade])
+    g_2b.parent_groups.set([g_2nd_grade])
+    g_2c.parent_groups.set([g_2nd_grade])
+    g_2nd_grade_french.parent_groups.set([g_2b, g_2c])
+
+    p_2a_1 = Person.objects.create(first_name="A", last_name="B")
+    p_2a_2 = Person.objects.create(first_name="A", last_name="B")
+    p_2b_1 = Person.objects.create(first_name="A", last_name="B")
+    p_2b_2 = Person.objects.create(first_name="A", last_name="B")
+    p_2c_1 = Person.objects.create(first_name="A", last_name="B")
+    p_2c_2 = Person.objects.create(first_name="A", last_name="B")
+    p_french_only = Person.objects.create(first_name="A", last_name="B")
+
+    g_2a.members.set([p_2a_1, p_2a_2])
+    g_2b.members.set([p_2b_1, p_2b_2])
+    g_2c.members.set([p_2c_1, p_2c_2])
+    g_2nd_grade_french.members.set([p_2b_1, p_2c_1, p_french_only])
+
+    assert g_2nd_grade in p_2a_1.member_of_recursive
+    assert g_2nd_grade in p_2a_2.member_of_recursive
+    assert g_2nd_grade in p_2b_1.member_of_recursive
+    assert g_2nd_grade in p_2b_2.member_of_recursive
+    assert g_2nd_grade in p_2c_1.member_of_recursive
+    assert g_2nd_grade in p_2c_2.member_of_recursive
+    assert g_2nd_grade in p_french_only.member_of_recursive
+    assert g_2b in p_french_only.member_of_recursive
+    assert g_2c in p_french_only.member_of_recursive
+
+
+def test_owners_recursive():
+    g_2nd_grade = Group.objects.create(name="2nd grade")
+    g_2a = Group.objects.create(name="2a")
+    g_2b = Group.objects.create(name="2b")
+
+    g_2a.parent_groups.set([g_2nd_grade])
+    g_2b.parent_groups.set([g_2nd_grade])
+
+    p_1 = Person.objects.create(first_name="A", last_name="B")
+    p_2 = Person.objects.create(first_name="A", last_name="B")
+
+    g_2nd_grade.owners.set([p_1])
+
+    assert p_1 in g_2a.owners_recursive
+    assert p_1 in g_2b.owners_recursive
+    assert p_2 not in g_2a.owners_recursive
+    assert p_2 not in g_2b.owners_recursive
+
+
+def test_owner_of_recursive():
+    g_2nd_grade = Group.objects.create(name="2nd grade")
+    g_2a = Group.objects.create(name="2a")
+    g_2b = Group.objects.create(name="2b")
+
+    g_2a.parent_groups.set([g_2nd_grade])
+    g_2b.parent_groups.set([g_2nd_grade])
+
+    p_1 = Person.objects.create(first_name="A", last_name="B")
+    p_2 = Person.objects.create(first_name="A", last_name="B")
+
+    g_2nd_grade.owners.set([p_1])
+
+    assert g_2a in p_1.owner_of_recursive.all()
+    assert g_2b in p_1.owner_of_recursive.all()
+    assert g_2a not in p_2.owner_of_recursive.all()
+    assert g_2b not in p_2.owner_of_recursive.all()
diff --git a/aleksis/core/tests/models/test_pdffile.py b/aleksis/core/tests/models/test_pdffile.py
index 1f1e936a8c72a6dca3c4796a18a69b9370adbb0a..d18e621d77043b3adafe93e5b18a04355950cfad 100644
--- a/aleksis/core/tests/models/test_pdffile.py
+++ b/aleksis/core/tests/models/test_pdffile.py
@@ -18,6 +18,7 @@ from aleksis.core.util.pdf import clean_up_expired_pdf_files
 pytestmark = pytest.mark.django_db
 
 
+@pytest.mark.skip
 @pytest.mark.usefixtures("celery_worker")
 @override_settings(CELERY_BROKER_URL="memory://localhost//")
 class PDFFIleTest(TransactionTestCase):
diff --git a/aleksis/core/tests/regression/test_regression.py b/aleksis/core/tests/regression/test_regression.py
new file mode 100644
index 0000000000000000000000000000000000000000..7ac541531db385ea8e28b454dc487458a45d7a66
--- /dev/null
+++ b/aleksis/core/tests/regression/test_regression.py
@@ -0,0 +1,35 @@
+def test_all_settigns_registered():
+    """Tests for regressions of preferences not being registered.
+
+    https://edugit.org/AlekSIS/official/AlekSIS-Core/-/issues/592
+    """
+
+    from dynamic_preferences.types import BasePreferenceType
+
+    from aleksis.core import preferences
+    from aleksis.core.preferences import person_preferences_registry, site_preferences_registry
+
+    for obj in preferences.__dict__.values():
+        if not isinstance(obj, BasePreferenceType):
+            continue
+
+        in_site_reg = site_preferences_registry.get(obj.section.name, {}).get(obj.name, None) is obj
+        in_person_reg = (
+            person_preferences_registry.get(obj.section.name, {}).get(obj.name, None) is obj
+        )
+
+        assert in_site_reg != in_person_reg
+
+
+def test_custom_managers_return_correct_qs():
+    """Tests that custom managers' get_queryset methods return the expected qs.
+
+    https://edugit.org/AlekSIS/official/AlekSIS-Core/-/issues/594
+    """
+
+    from aleksis.core import managers
+
+    def _check_get_queryset(Manager, QuerySet):
+        assert isinstance(Manager.from_queryset(QuerySet)().get_queryset(), QuerySet)
+
+    _check_get_queryset(managers.GroupManager, managers.GroupQuerySet)
diff --git a/aleksis/core/tests/views/test_account.py b/aleksis/core/tests/views/test_account.py
index 28686eabf8290a7a72444d8fe5f4b71bb74f4626..ae598ab72826d403e94531ef2bf7194b4ee5225c 100644
--- a/aleksis/core/tests/views/test_account.py
+++ b/aleksis/core/tests/views/test_account.py
@@ -1,10 +1,23 @@
 from django.conf import settings
+from django.test import override_settings
 from django.urls import reverse
 
+import ldap
 import pytest
+from django_auth_ldap.config import LDAPSearch
+
+from aleksis.core.models import UserAdditionalAttributes
 
 pytestmark = pytest.mark.django_db
 
+LDAP_BASE = "dc=example,dc=com"
+LDAP_SETTINGS = {
+    "AUTH_LDAP_GLOBAL_OPTIONS": {
+        ldap.OPT_NETWORK_TIMEOUT: 1,
+    },
+    "AUTH_LDAP_USER_SEARCH": LDAPSearch(LDAP_BASE, ldap.SCOPE_SUBTREE),
+}
+
 
 def test_index_not_logged_in(client):
     response = client.get("/")
@@ -40,3 +53,34 @@ def test_logout(client, django_user_model):
 
     assert response.status_code == 200
     assert "Please login to see this page." in response.content.decode("utf-8")
+
+
+@override_settings(
+    AUTHENTICATION_BACKENDS=[
+        "aleksis.core.util.ldap.LDAPBackend",
+        "django.contrib.auth.backends.ModelBackend",
+    ],
+    AUTH_LDAP_SERVER_URI="ldap://[100::0]",
+    AUTH_LDAP_SET_USABLE_PASSWORD=True,
+    **LDAP_SETTINGS
+)
+def test_login_ldap_fail_if_previously_ldap_authenticated(client, django_user_model):
+    username = "foo"
+    password = "bar"
+
+    django_user_model.objects.create_user(username=username, password=password)
+
+    # Logging in with a fresh account should success
+    res = client.login(username=username, password=password)
+    assert res
+    client.get(reverse("logout"), follow=True)
+
+    # Logging in with a previously LDAP-authenticated account should fail
+    UserAdditionalAttributes.set_user_attribute(username, "ldap_authenticated", True)
+    res = client.login(username=username, password=password)
+    assert not res
+
+    # Explicitly noting account has not been used with LDAP should succeed
+    UserAdditionalAttributes.set_user_attribute(username, "ldap_authenticated", False)
+    res = client.login(username=username, password=password)
+    assert res
diff --git a/aleksis/core/urls.py b/aleksis/core/urls.py
index 0c00bb8b26b397f2b0bdfb591bd232fcb3d13e74..b5dcb004d040b64b22242b977702a104fb2ea33a 100644
--- a/aleksis/core/urls.py
+++ b/aleksis/core/urls.py
@@ -23,6 +23,7 @@ urlpatterns = [
     path("serviceworker.js", views.ServiceWorkerView.as_view(), name="service_worker"),
     path("offline/", views.OfflineView.as_view(), name="offline"),
     path("about/", views.about, name="about_aleksis"),
+    path("accounts/signup/", views.AccountRegisterView.as_view(), name="account_signup"),
     path("accounts/logout/", auth_views.LogoutView.as_view(), name="logout"),
     path(
         "accounts/password/change/",
@@ -30,6 +31,16 @@ urlpatterns = [
         name="account_change_password",
     ),
     path("accounts/", include("allauth.urls")),
+    path("invitations/send-invite", views.InvitePerson.as_view(), name="invite_person"),
+    path(
+        "invitations/code/enter", views.EnterInvitationCode.as_view(), name="enter_invitation_code"
+    ),
+    path(
+        "invitations/code/generate",
+        views.GenerateInvitationCode.as_view(),
+        name="generate_invitation_code",
+    ),
+    path("invitations/", include("invitations.urls")),
     path(
         "accounts/social/connections/<int:pk>/delete",
         views.SocialAccountDeleteView.as_view(),
@@ -39,6 +50,7 @@ urlpatterns = [
     path("admin/uwsgi/", include("django_uwsgi.urls")),
     path("data_management/", views.data_management, name="data_management"),
     path("status/", views.SystemStatus.as_view(), name="system_status"),
+    path("account/login/", views.LoginView.as_view(), name="login"),
     path("", include(tf_urls)),
     path("celery_progress/<str:task_id>/", views.CeleryProgressView.as_view(), name="task_status"),
     path("accounts/logout/", auth_views.LogoutView.as_view(), name="logout"),
@@ -51,6 +63,7 @@ urlpatterns = [
     path("person/<int:id_>/", views.person, name="person_by_id"),
     path("person/<int:pk>/edit/", views.EditPersonView.as_view(), name="edit_person_by_id"),
     path("person/<int:id_>/delete/", views.delete_person, name="delete_person_by_id"),
+    path("person/<int:id_>/invite/", views.InvitePersonByID.as_view(), name="invite_person_by_id"),
     path("groups", views.groups, name="groups"),
     path("groups/additional_fields", views.additional_fields, name="additional_fields"),
     path("groups/child_groups/", views.groups_child_groups, name="groups_child_groups"),
@@ -121,6 +134,11 @@ urlpatterns = [
         views.OAuth2EditView.as_view(),
         name="edit_oauth2_application",
     ),
+    path(
+        "oauth/authorize/",
+        views.CustomAuthorizationView.as_view(),
+        name="oauth2_provider:authorize",
+    ),
     path("oauth/", include("oauth2_provider.urls", namespace="oauth2_provider")),
     path("__i18n__/", include("django.conf.urls.i18n")),
     path(
diff --git a/aleksis/core/util/core_helpers.py b/aleksis/core/util/core_helpers.py
index 3dcebe4df7d97fa7fed8db4efda25362bee16210..3b3c934a85341e75092b6ef1e8df77bdc838687e 100644
--- a/aleksis/core/util/core_helpers.py
+++ b/aleksis/core/util/core_helpers.py
@@ -3,7 +3,7 @@ from datetime import datetime, timedelta
 from importlib import import_module, metadata
 from itertools import groupby
 from operator import itemgetter
-from typing import Any, Callable, Optional, Sequence, Union
+from typing import Any, Callable, Dict, Optional, Sequence, Union
 from warnings import warn
 
 from django.conf import settings
@@ -13,7 +13,9 @@ from django.db.models import Model, QuerySet
 from django.http import HttpRequest
 from django.shortcuts import get_object_or_404
 from django.utils import timezone
+from django.utils.crypto import get_random_string
 from django.utils.functional import lazy
+from django.utils.module_loading import import_string
 
 from cache_memoize import cache_memoize
 
@@ -198,7 +200,7 @@ def has_person(obj: Union[HttpRequest, Model]) -> bool:
         return True
 
 
-def custom_information_processor(request: HttpRequest) -> dict:
+def custom_information_processor(request: Union[HttpRequest, None]) -> dict:
     """Provide custom information in all templates."""
     from ..models import CustomMenu
 
@@ -278,6 +280,11 @@ def queryset_rules_filter(
     return queryset.filter(pk__in=wanted_objects)
 
 
+def generate_random_code(length, packet_size) -> str:
+    """Generate random code for e.g. invitations."""
+    return get_random_string(packet_size * length).lower()
+
+
 def unread_notifications_badge(request: HttpRequest) -> int:
     """Generate badge content with the number of unread notifications."""
     return request.user.person.unread_notifications_count
@@ -316,3 +323,12 @@ def get_allowed_object_ids(request: HttpRequest, models: list) -> list:
         ]
 
     return allowed_object_ids
+
+
+def process_custom_context_processors(context_processors: list) -> Dict[str, Any]:
+    """Process custom context processors."""
+    context = {}
+    processors = tuple(import_string(path) for path in context_processors)
+    for processor in processors:
+        context.update(processor(None))
+    return context
diff --git a/aleksis/core/util/ldap.py b/aleksis/core/util/ldap.py
index 96b058ac0060975cdd08281be94ba4df371aa7db..b60c9de98a48ca8b34a1cbd758301f0542cc9116 100644
--- a/aleksis/core/util/ldap.py
+++ b/aleksis/core/util/ldap.py
@@ -4,6 +4,8 @@ from django.core.exceptions import PermissionDenied
 
 from django_auth_ldap.backend import LDAPBackend as _LDAPBackend
 
+from ..models import UserAdditionalAttributes
+
 
 class LDAPBackend(_LDAPBackend):
     default_settings = {"SET_USABLE_PASSWORD": False}
@@ -24,11 +26,28 @@ class LDAPBackend(_LDAPBackend):
 
         if self.settings.SET_USABLE_PASSWORD:
             if not user:
-                # Fail early and do not try other backends
-                raise PermissionDenied("LDAP failed to authenticate user")
+                # The user could not be authenticated against LDAP.
+                # We need to make sure to let other backends handle it, but also that
+                # we do not let actually deleted/locked LDAP users fall through to a
+                # backend that cached a valid password
+                if UserAdditionalAttributes.get_user_attribute(
+                    ldap_user._username, "ldap_authenticated", False
+                ):
+                    # User was LDAP-authenticated in the past, so we fail authentication now
+                    # to not let other backends override a legitimate deletion
+                    raise PermissionDenied("LDAP failed to authenticate user")
+                else:
+                    # No note about LDAP authentication in the past
+                    # The user can continue authentication like before if they exist
+                    return user
 
             # Set a usable password so users can change their LDAP password
             user.set_password(password)
             user.save()
 
+            # Not that we LDAP-autenticated the user so we can check this in the future
+            UserAdditionalAttributes.set_user_attribute(
+                ldap_user._username, "ldap_authenticated", True
+            )
+
         return user
diff --git a/aleksis/core/util/notifications.py b/aleksis/core/util/notifications.py
index 38d27057033752d6ae7cdefce9c53b4728447b80..dac344b7a2e2877d65ff1bc80b580b860bf1067f 100644
--- a/aleksis/core/util/notifications.py
+++ b/aleksis/core/util/notifications.py
@@ -21,7 +21,7 @@ except ImportError:
 def send_templated_sms(
     template_name: str, from_number: str, recipient_list: Sequence[str], context: dict
 ) -> None:
-    """Render a plan-text template and send via SMS to all recipients."""
+    """Render a plain-text template and send via SMS to all recipients."""
     template = get_template(template_name)
     text = template.render(context)
 
diff --git a/aleksis/core/util/pdf.py b/aleksis/core/util/pdf.py
index 3f27680d9d8b3fc569a457ec78a2ce7c154c23d1..d65000811b123eab7ce6ad7e7ce29b02c3d73b06 100644
--- a/aleksis/core/util/pdf.py
+++ b/aleksis/core/util/pdf.py
@@ -1,7 +1,7 @@
 import os
 import subprocess  # noqa
 from tempfile import TemporaryDirectory
-from typing import Optional, Tuple
+from typing import Optional, Tuple, Union
 from urllib.parse import urljoin
 
 from django.conf import settings
@@ -22,6 +22,7 @@ from celery_progress.backend import ProgressRecorder
 from aleksis.core.celery import app
 from aleksis.core.models import PDFFile
 from aleksis.core.util.celery_progress import recorded_task, render_progress_page
+from aleksis.core.util.core_helpers import process_custom_context_processors
 
 
 @recorded_task
@@ -71,7 +72,12 @@ def generate_pdf_from_template(
     template_name: str, context: Optional[dict] = None, request: Optional[HttpRequest] = None
 ) -> Tuple[PDFFile, AsyncResult]:
     """Start a PDF generation task and return the matching file object and Celery result."""
-    html_template = render_to_string(template_name, context, request)
+    if not request:
+        processed_context = process_custom_context_processors(settings.PDF_CONTEXT_PROCESSORS)
+        processed_context.update(context)
+    else:
+        processed_context = context
+    html_template = render_to_string(template_name, processed_context, request)
 
     file_object = PDFFile.objects.create(html_file=ContentFile(html_template, name="source.html"))
 
@@ -87,7 +93,9 @@ def generate_pdf_from_template(
     return file_object, result
 
 
-def render_pdf(request: HttpRequest, template_name: str, context: dict = None) -> HttpResponse:
+def render_pdf(
+    request: Union[HttpRequest, None], template_name: str, context: dict = None
+) -> HttpResponse:
     """Start PDF generation and show progress page.
 
     The progress page will redirect to the PDF after completion.
diff --git a/aleksis/core/util/sass_helpers.py b/aleksis/core/util/sass_helpers.py
index 2579ed83a6f7d7f143456e227f1c662987ba3f55..21e3d0bacd0700dc7c0db74182e547e6c885172c 100644
--- a/aleksis/core/util/sass_helpers.py
+++ b/aleksis/core/util/sass_helpers.py
@@ -23,7 +23,7 @@ def get_preference(section: str, name: str) -> str:
 def clean_scss(*args, **kwargs) -> None:
     """Unlink compiled CSS (i.e. cache invalidation)."""
     sass_storage = SassFileStorage()
-    __, files = sass_storage.listdir("")
+    __, files = sass_storage.listdir("public")
 
     for source_map in filter(lambda x: x.endswith(".css.map"), files):
-        sass_storage.delete(source_map)
+        sass_storage.delete(f"public/{source_map}")
diff --git a/aleksis/core/views.py b/aleksis/core/views.py
index a1e1177150c11cb50f1012cebad1c1128dbc14da..49ef723df8d5fdb27d810c8668d9ec3559d9272e 100644
--- a/aleksis/core/views.py
+++ b/aleksis/core/views.py
@@ -1,5 +1,6 @@
+from textwrap import wrap
 from typing import Any, Dict, Optional, Type
-from urllib.parse import urlencode
+from urllib.parse import urlencode, urlparse, urlunparse
 
 from django.apps import apps
 from django.conf import settings
@@ -14,27 +15,28 @@ from django.http import (
     Http404,
     HttpRequest,
     HttpResponse,
-    HttpResponseNotFound,
     HttpResponseRedirect,
     HttpResponseServerError,
     JsonResponse,
+    QueryDict,
 )
 from django.shortcuts import get_object_or_404, redirect, render
 from django.template import loader
 from django.urls import reverse, reverse_lazy
+from django.utils import timezone
 from django.utils.decorators import method_decorator
 from django.utils.translation import get_language
 from django.utils.translation import gettext_lazy as _
 from django.views.decorators.cache import never_cache
 from django.views.defaults import ERROR_500_TEMPLATE_NAME
-from django.views.generic import FormView
 from django.views.generic.base import TemplateView, View
 from django.views.generic.detail import DetailView, SingleObjectMixin
-from django.views.generic.edit import DeleteView
+from django.views.generic.edit import DeleteView, FormView
 from django.views.generic.list import ListView
 
 import reversion
-from allauth.account.views import PasswordChangeView
+from allauth.account.utils import _has_verified_for_login, send_email_confirmation
+from allauth.account.views import PasswordChangeView, SignupView
 from allauth.socialaccount.adapter import get_adapter
 from allauth.socialaccount.models import SocialAccount
 from celery_progress.views import get_progress
@@ -48,9 +50,15 @@ from haystack.inputs import AutoQuery
 from haystack.query import SearchQuerySet
 from haystack.utils.loading import UnifiedIndex
 from health_check.views import MainView
+from invitations.views import SendInvite, accept_invitation
+from oauth2_provider.exceptions import OAuthToolkitError
+from oauth2_provider.models import get_application_model
+from oauth2_provider.views import AuthorizationView
 from reversion import set_user
 from reversion.views import RevisionMixin
+from rules import test_rule
 from rules.contrib.views import PermissionRequiredMixin, permission_required
+from two_factor.views.core import LoginView as AllAuthLoginView
 
 from aleksis.core.data_checks import DataCheckRegistry, check_data
 
@@ -64,6 +72,7 @@ from .filters import (
     UserObjectPermissionFilter,
 )
 from .forms import (
+    AccountRegisterForm,
     AnnouncementForm,
     AssignPermissionForm,
     ChildGroupsForm,
@@ -72,6 +81,7 @@ from .forms import (
     EditGroupForm,
     EditGroupTypeForm,
     GroupPreferenceForm,
+    InvitationCodeForm,
     OAuthApplicationForm,
     PersonForm,
     PersonPreferenceForm,
@@ -93,6 +103,7 @@ from .models import (
     OAuthApplication,
     PDFFile,
     Person,
+    PersonInvitation,
     SchoolTerm,
     TaskUserAssignment,
 )
@@ -108,6 +119,7 @@ from .tables import (
     GroupObjectPermissionTable,
     GroupsTable,
     GroupTypesTable,
+    InvitationsTable,
     PersonsTable,
     SchoolTermTable,
     UserGlobalPermissionTable,
@@ -117,6 +129,7 @@ from .util import messages
 from .util.apps import AppConfig
 from .util.celery_progress import render_progress_page
 from .util.core_helpers import (
+    generate_random_code,
     get_allowed_object_ids,
     get_pwa_icons,
     get_site_preferences,
@@ -288,9 +301,7 @@ def persons(request: HttpRequest) -> HttpResponse:
     context = {}
 
     # Get all persons
-    persons = get_objects_for_user(
-        request.user, "core.view_person", Person.objects.filter(is_active=True)
-    )
+    persons = get_objects_for_user(request.user, "core.view_person", Person.objects.all())
 
     # Get filter
     persons_filter = PersonFilter(request.GET, queryset=persons)
@@ -337,7 +348,7 @@ def group(request: HttpRequest, id_: int) -> HttpResponse:
     group = Group.objects.get(pk=id_)
 
     # Get members
-    members = group.members.filter(is_active=True)
+    members = group.members.all()
 
     # Build table
     members_table = PersonsTable(members)
@@ -345,7 +356,7 @@ def group(request: HttpRequest, id_: int) -> HttpResponse:
     context["members_table"] = members_table
 
     # Get owners
-    owners = group.owners.filter(is_active=True)
+    owners = group.owners.all()
 
     # Build table
     owners_table = PersonsTable(owners)
@@ -663,11 +674,14 @@ def preferences(
             raise PermissionDenied()
     else:
         # Invalid registry name passed from URL
-        return HttpResponseNotFound()
+        raise Http404(_("The requested preference registry does not exist"))
 
     if not section and len(registry.sections()) > 0:
         default_section = list(registry.sections())[0]
-        return redirect(f"preferences_{registry_name}", default_section)
+        if instance:
+            return redirect(f"preferences_{registry_name}", instance.pk, default_section)
+        else:
+            return redirect(f"preferences_{registry_name}", default_section)
 
     # Build final form from dynamic-preferences
     form_class = preference_form_builder(form_class, instance=instance, section=section)
@@ -900,7 +914,7 @@ class SolveDataCheckView(PermissionRequiredMixin, RevisionMixin, DetailView):
             messages.success(request, msg)
             return redirect("check_data")
         else:
-            return HttpResponseNotFound()
+            raise Http404(_("The requested solve option does not exist"))
 
 
 class DashboardWidgetListView(PermissionRequiredMixin, SingleTableView):
@@ -1051,6 +1065,72 @@ class EditDashboardView(PermissionRequiredMixin, View):
         return render(request, "core/edit_dashboard.html", context=context)
 
 
+class InvitePerson(PermissionRequiredMixin, SingleTableView, SendInvite):
+    """View to invite a person to register an account."""
+
+    template_name = "invitations/forms/_invite.html"
+    permission_required = "core.can_invite"
+    model = PersonInvitation
+    table_class = InvitationsTable
+    context = {}
+
+    # Get queryset of invitations
+    def get_context_data(self, **kwargs):
+        queryset = kwargs.pop("object_list", None)
+        if queryset is None:
+            self.object_list = self.model.objects.all()
+        return super().get_context_data(**kwargs)
+
+
+class EnterInvitationCode(FormView):
+    """View to enter an invitation code."""
+
+    template_name = "invitations/enter.html"
+    form_class = InvitationCodeForm
+
+    def form_valid(self, form):
+        code = "".join(form.cleaned_data["code"].lower().split("-"))
+        # Check if valid invitations exists
+        if (
+            PersonInvitation.objects.filter(key=code).exists()
+            and not PersonInvitation.objects.get(key=code).accepted
+            and not PersonInvitation.objects.get(key=code).key_expired()
+        ):
+            invitation = PersonInvitation.objects.get(key=code)
+            # Mark invitation as accepted and redirect to signup
+            accept_invitation(
+                invitation=invitation, request=self.request, signal_sender=self.request.user
+            )
+            return redirect("account_signup")
+        return redirect("invitations:accept-invite", code)
+
+
+class GenerateInvitationCode(View):
+    """View to generate an invitation code."""
+
+    def get(self, request):
+        # Build code
+        length = get_site_preferences()["auth__invite_code_length"]
+        packet_size = get_site_preferences()["auth__invite_code_packet_size"]
+        code = generate_random_code(length, packet_size)
+
+        # Create invitation object
+        invitation = PersonInvitation.objects.create(
+            email="", inviter=request.user, key=code, sent=timezone.now()
+        )
+
+        # Make code more readable
+        code = "-".join(wrap(invitation.key, 5))
+
+        # Generate success message and print code
+        messages.success(
+            request,
+            _(f"The invitation was successfully created. The invitation code is {code}"),
+        )
+
+        return redirect("invite_person")
+
+
 class PermissionsListBaseView(PermissionRequiredMixin, SingleTableMixin, FilterView):
     """Base view for list of all permissions."""
 
@@ -1244,7 +1324,7 @@ class RedirectToPDFFile(SingleObjectMixin, View):
     def get(self, *args, **kwargs):
         file_object = self.get_object()
         if not file_object.file:
-            raise Http404()
+            raise Http404(_("The requested PDF file does not exist"))
         return redirect(file_object.file.url)
 
 
@@ -1253,11 +1333,11 @@ class CeleryProgressView(View):
 
     def get(self, request: HttpRequest, task_id: str, *args, **kwargs) -> HttpResponse:
         if request.user.is_anonymous:
-            raise Http404()
+            raise Http404(_("The requested task does not exist or is not accessible"))
         if not TaskUserAssignment.objects.filter(
             task_result__task_id=task_id, user=request.user
         ).exists():
-            raise Http404()
+            raise Http404(_("The requested task does not exist or is not accessible"))
         return get_progress(request, task_id, *args, **kwargs)
 
 
@@ -1315,3 +1395,109 @@ def server_error(
     context = {"request": request}
 
     return HttpResponseServerError(template.render(context))
+
+
+class AccountRegisterView(SignupView):
+    """Custom view to register a user account.
+
+    Rewrites dispatch function from allauth to check if signup is open or if the user
+    has a verified email address from an invitation; otherwise raises permission denied.
+    """
+
+    form_class = AccountRegisterForm
+    success_url = "index"
+
+    def dispatch(self, request, *args, **kwargs):
+        if not test_rule("core.can_register") and not request.session.get("account_verified_email"):
+            raise PermissionDenied()
+        return super(AccountRegisterView, self).dispatch(request, *args, **kwargs)
+
+    def get_form_kwargs(self):
+        kwargs = super(AccountRegisterView, self).get_form_kwargs()
+        kwargs["request"] = self.request
+        return kwargs
+
+
+class InvitePersonByID(View):
+    """Custom view to invite person by their ID."""
+
+    success_url = reverse_lazy("persons")
+
+    def get(self, request, *args, **kwargs):
+        self.object = self.get_object()
+        success_url = reverze_lazy("person_by_id", self.object.pk)
+        person = self.object
+
+        if not PersonInvitation.objects.filter(email=person.email).exists():
+            length = get_site_preferences()["auth__invite_code_length"]
+            packet_size = get_site_preferences()["auth__invite_code_packet_size"]
+            key = generate_random_code(length, packet_size)
+            invite = PersonInvitation.objects.create(person=person, key=key)
+            if person.email:
+                invite.email = person.email
+            invite.inviter = self.request.user
+            invite.save()
+
+            invite.send_invitation(self.request)
+            messages.success(self.request, _("Person was invited successfully."))
+        else:
+            messages.success(self.request, _("Person was already invited."))
+
+        return HttpResponseRedirect(success_url)
+
+
+class LoginView(AllAuthLoginView):
+    """Custom login view covering e-mail verification if mandatory.
+
+    Overrides view from allauth to check if email verification from django-invitations is
+    mandatory. If it i, checks if the user has a verified email address, if not,
+    it re-sends verification.
+    """
+
+    def done(self, form_list, **kwargs):
+        if settings.ACCOUNT_EMAIL_VERIFICATION == "mandatory":
+            user = self.get_user()
+            if not _has_verified_for_login(user, user.email):
+                send_email_confirmation(self.request, user, signup=False, email=user.email)
+                return render(self.request, "account/verification_sent.html")
+
+        return super().done(form_list, **kwargs)
+
+    def get_context_data(self, form, **kwargs):
+        """Override context data to hide side menu and include OAuth2 application if given."""
+        context = super().get_context_data(form, **kwargs)
+        if self.request.GET.get("oauth"):
+            context["no_menu"] = True
+
+            if self.request.GET.get("client_id"):
+                application = get_application_model().objects.get(
+                    client_id=self.request.GET["client_id"]
+                )
+                context["oauth_application"] = application
+        return context
+
+
+class CustomAuthorizationView(AuthorizationView):
+    def handle_no_permission(self):
+        """Override handle_no_permission to provide OAuth2 information to login page."""
+        redirect_obj = super().handle_no_permission()
+
+        try:
+            scopes, credentials = self.validate_authorization_request(self.request)
+        except OAuthToolkitError as error:
+            # Application is not available at this time.
+            return self.error_response(error, application=None)
+
+        login_url_parts = list(urlparse(redirect_obj.url))
+        querystring = QueryDict(login_url_parts[4], mutable=True)
+        querystring["oauth"] = "yes"
+        querystring["client_id"] = credentials["client_id"]
+        login_url_parts[4] = querystring.urlencode(safe="/")
+
+        return HttpResponseRedirect(urlunparse(login_url_parts))
+
+    def get_context_data(self, **kwargs):
+        """Override context data to hide side menu."""
+        context = super().get_context_data(**kwargs)
+        context["no_menu"] = True
+        return context
diff --git a/docs/_static/create_social_application.png b/docs/_static/create_social_application.png
new file mode 100644
index 0000000000000000000000000000000000000000..c28c5c30a6d71f8aa0f1177b92048449c688d113
Binary files /dev/null and b/docs/_static/create_social_application.png differ
diff --git a/docs/admin/01_install.rst b/docs/admin/01_install.rst
index 5251a054be53d1e587fbadc0c9c4b848d25df249..6d1984b96e103d90d33496f4e520b7049e748a1a 100644
--- a/docs/admin/01_install.rst
+++ b/docs/admin/01_install.rst
@@ -164,6 +164,7 @@ First, you should get a TLS certificate, e.g. by using `Let's Encrypt`_.
 Then, create a virtual host in nginx, by editing `/etc/nginx/sites-available/aleksis.example.com`.
 
 .. code-block:: nginx
+
    upstream aleksis {
      server unix:///run/uwsgi/app/aleksis/socket;
    }
diff --git a/docs/admin/03_socialaccounts.rst b/docs/admin/03_socialaccounts.rst
new file mode 100644
index 0000000000000000000000000000000000000000..97b023947a27768b825c693b7c5cd8bff938a49e
--- /dev/null
+++ b/docs/admin/03_socialaccounts.rst
@@ -0,0 +1,34 @@
+Social accounts
+===============
+
+AlekSIS can authenticate users against third party applications using OAuth2
+or OpenID.
+
+
+.. warning::
+  Social accounts are **not** working with two factor authentication! If a user
+  authenticates with a social account, the two factor authentication is
+  ignored on login (but enforced for views that require two factor authentication later).
+
+Configuring social account provider
+-----------------------------------
+
+For available providers, see documentation of `django-allauth
+<https://django-allauth.readthedocs.io/en/latest/providers.html>`_.
+
+A new social account provider can be configured in your configuration file
+(located in ``/etc/aleksis/``).
+
+Configuration example::
+
+  [auth.providers.gitlab]
+  GITLAB_URL = "https://gitlab.exmaple.com"
+
+After configuring a new auth provider, you have to restart AlekSIS and configure client id and secret in the Backend Admin interface.
+Click "Social applications" and add a new application. Choose your
+provider and enter client id and secret from your application and choose
+your site:
+
+.. image:: ../_static/create_social_application.png
+  :width: 400
+  :alt: Create social application
diff --git a/docs/dev/01_setup.rst b/docs/dev/01_setup.rst
index dd3121a822e53c603b21511b790f5ef6950f295a..24a64dbf7b7536193f7691538b2846409782519d 100644
--- a/docs/dev/01_setup.rst
+++ b/docs/dev/01_setup.rst
@@ -20,7 +20,7 @@ AlekSIS requires `PostgreSQL`_ (version 13 or newer) as database
 backend. To provide a database names `aleksis` with a user named
 `aleksis` on Debian::
 
-  sudo apt install postgresql-13
+  sudo apt install postgresql
   sudo -u postgres createuser -P aleksis
   sudo -u postgres createdb -O aleksis aleksis
 
@@ -86,16 +86,15 @@ some maintenance tasks need to be done:
 2. Collect static files
 3. Run database migrations
 
-All three steps can be done with the ``poetry run`` command and
+All three steps can be done with the ``poetry shell`` command and
 ``aleksis-admin``::
 
-  poetry run aleksis-admin yarn install
-  poetry run aleksis-admin collectstatic
-  poetry run aleksis-admin compilemessages
-  poetry run aleksis-admin migrate
-  poetry run aleksis-admin createinitialrevisions
-
-(You might need database settings for the `migrate` command; see below.)
+  ALEKSIS_maintenance__debug=true ALEKSIS_database__password=aleksis poetry shell
+   poetry run aleksis-admin yarn install
+   poetry run aleksis-admin collectstatic
+   poetry run aleksis-admin compilemessages
+   poetry run aleksis-admin migrate
+   poetry run aleksis-admin createinitialrevisions
 
 Running the development server
 ------------------------------
@@ -108,7 +107,7 @@ basic settings in as environment variable. Here is an example that runs the
 development server against a local PostgreSQL database with password
 `aleksis` (all else remains default) and with the `debug` setting enabled::
 
-  ALEKSIS_debug=true ALEKSIS_database__password=aleksis poetry run aleksis-admin runuwsgi
+  ALEKSIS_maintenance__debug=true ALEKSIS_database__password=aleksis poetry run aleksis-admin runuwsgi
 
 .. figure:: /screenshots/index.png
    :scale: 50%
diff --git a/docs/dev/04_materialize_templates.rst b/docs/dev/04_materialize_templates.rst
index 3e7979cf1d39c317bb02832d43c84ab970b9d7a1..882604168d04ae6fd6085626305844a8648405e7 100644
--- a/docs/dev/04_materialize_templates.rst
+++ b/docs/dev/04_materialize_templates.rst
@@ -111,7 +111,6 @@ In your ``forms.py`` you can configure the layout of the fields like in the Edit
             _("Base data"),
             "short_name",
             Row("user", "primary_group"),
-            "is_active",
             Row("first_name", "additional_name", "last_name"),
         ),
         Fieldset(_("Address"), Row("street", "housenumber"), Row("postal_code", "place")),
@@ -136,4 +135,4 @@ After you've loaded the template tag, you can simply generate the table like thi
 
 .. _MaterializeCSS: https://materializecss.com/
 .. _django-material: https://pypi.org/project/django-material/
-.. _Extended Navbar with Tabs: https://materializecss.com/navbar.html#navbar-tabs
\ No newline at end of file
+.. _Extended Navbar with Tabs: https://materializecss.com/navbar.html#navbar-tabs
diff --git a/poetry.lock b/poetry.lock
deleted file mode 100644
index 0ba1edc825a78164060b6f3c280b9532ffd96d8b..0000000000000000000000000000000000000000
--- a/poetry.lock
+++ /dev/null
@@ -1,5578 +0,0 @@
-[[package]]
-name = "alabaster"
-version = "0.7.12"
-description = "A configurable sidebar-enabled Sphinx theme"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "aleksis-builddeps"
-version = "5+20211130152626.23302bab"
-description = "AlekSIS (School Information System) — Build/Dev dependencies for apps"
-category = "dev"
-optional = false
-python-versions = ">=3.9,<4.0"
-
-[package.dependencies]
-black = ">=21.10b0,<22.0"
-curlylint = ">=0.13.0,<0.14.0"
-django-stubs = ">=1.1,<2.0"
-flake8 = ">=4.0.0,<5.0.0"
-flake8-bandit = ">=2.1.2,<3.0.0"
-flake8-black = ">=0.2.0,<0.3.0"
-flake8-builtins = ">=1.4.1,<2.0.0"
-flake8-django = ">=1.0.0,<2.0.0"
-flake8-docstrings = ">=1.5.0,<2.0.0"
-flake8-fixme = ">=1.1.1,<2.0.0"
-flake8-isort = ">=4.0.0,<5.0.0"
-flake8-mypy = ">=17.8.0,<18.0.0"
-flake8-rst-docstrings = ">=0.2.0,<0.3.0"
-freezegun = ">=1.1.0,<2.0.0"
-isort = ">=5.0.0,<6.0.0"
-pytest = ">=6.0,<7.0"
-pytest-cov = ">=3.0.0,<4.0.0"
-pytest-django = ">=4.1,<5.0"
-pytest-django-testing-postgresql = ">=0.1,<0.2"
-pytest-sugar = ">=0.9.2,<0.10.0"
-safety = ">=1.8.5,<2.0.0"
-selenium = ">=4.0.0,<5.0.0"
-sphinx = ">=3.0,<4.0"
-sphinx-autodoc-typehints = ">=1.7,<2.0"
-sphinx_materialdesign_theme = ">=0.1.11,<0.2.0"
-sphinxcontrib-django = ">=0.5.0,<0.6.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "amqp"
-version = "5.0.6"
-description = "Low-level AMQP client for Python (fork of amqplib)."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-vine = "5.0.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "appnope"
-version = "0.1.2"
-description = "Disable App Nap on macOS >= 10.9"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "asgiref"
-version = "3.4.1"
-description = "ASGI specs, helper code, and adapters"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.extras]
-tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "asn1crypto"
-version = "1.4.0"
-description = "Fast ASN.1 parser and serializer with definitions for private keys, public keys, certificates, CRL, OCSP, CMS, PKCS#3, PKCS#7, PKCS#8, PKCS#12, PKCS#5, X.509 and TSP"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "async-generator"
-version = "1.10"
-description = "Async generators and context managers for Python 3.5+"
-category = "dev"
-optional = false
-python-versions = ">=3.5"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "atomicwrites"
-version = "1.4.0"
-description = "Atomic file writes."
-category = "dev"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "attrs"
-version = "21.2.0"
-description = "Classes Without Boilerplate"
-category = "dev"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-
-[package.extras]
-dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
-docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
-tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
-tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "babel"
-version = "2.9.1"
-description = "Internationalization utilities"
-category = "dev"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-
-[package.dependencies]
-pytz = ">=2015.7"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "backcall"
-version = "0.2.0"
-description = "Specifications for callback functions passed in to an API"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "bandit"
-version = "1.7.1"
-description = "Security oriented static analyser for python code."
-category = "dev"
-optional = false
-python-versions = ">=3.5"
-
-[package.dependencies]
-colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""}
-GitPython = ">=1.0.1"
-PyYAML = ">=5.3.1"
-stevedore = ">=1.20.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "beautifulsoup4"
-version = "4.10.0"
-description = "Screen-scraping library"
-category = "main"
-optional = false
-python-versions = ">3.0.0"
-
-[package.dependencies]
-soupsieve = ">1.2"
-
-[package.extras]
-html5lib = ["html5lib"]
-lxml = ["lxml"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "billiard"
-version = "3.6.4.0"
-description = "Python multiprocessing fork with improvements and bugfixes"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "black"
-version = "21.11b1"
-description = "The uncompromising code formatter."
-category = "dev"
-optional = false
-python-versions = ">=3.6.2"
-
-[package.dependencies]
-click = ">=7.1.2"
-mypy-extensions = ">=0.4.3"
-pathspec = ">=0.9.0,<1"
-platformdirs = ">=2"
-regex = ">=2021.4.4"
-tomli = ">=0.2.6,<2.0.0"
-typing-extensions = [
-    {version = ">=3.10.0.0", markers = "python_version < \"3.10\""},
-    {version = "!=3.10.0.1", 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)"]
-python2 = ["typed-ast (>=1.4.3)"]
-uvloop = ["uvloop (>=0.15.2)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "bleach"
-version = "4.1.0"
-description = "An easy safelist-based HTML-sanitizing tool."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-packaging = "*"
-six = ">=1.9.0"
-webencodings = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "boolean.py"
-version = "3.8"
-description = "Define boolean algebras, create and parse boolean expressions and create custom boolean DSL."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "boto3"
-version = "1.20.17"
-description = "The AWS SDK for Python"
-category = "main"
-optional = true
-python-versions = ">= 3.6"
-
-[package.dependencies]
-botocore = ">=1.23.17,<1.24.0"
-jmespath = ">=0.7.1,<1.0.0"
-s3transfer = ">=0.5.0,<0.6.0"
-
-[package.extras]
-crt = ["botocore[crt] (>=1.21.0,<2.0a0)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "botocore"
-version = "1.23.17"
-description = "Low-level, data-driven core of boto 3."
-category = "main"
-optional = true
-python-versions = ">= 3.6"
-
-[package.dependencies]
-jmespath = ">=0.7.1,<1.0.0"
-python-dateutil = ">=2.1,<3.0.0"
-urllib3 = ">=1.25.4,<1.27"
-
-[package.extras]
-crt = ["awscrt (==0.12.5)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "bs4"
-version = "0.0.1"
-description = "Screen-scraping library"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-beautifulsoup4 = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "calendarweek"
-version = "0.5.0"
-description = "Utilities for working with calendar weeks in Python and Django"
-category = "main"
-optional = false
-python-versions = ">=3.7,<4.0"
-
-[package.extras]
-django = ["Django (>=2.2,<4.0)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "celery"
-version = "5.1.2"
-description = "Distributed Task Queue."
-category = "main"
-optional = false
-python-versions = ">=3.6,"
-
-[package.dependencies]
-billiard = ">=3.6.4.0,<4.0"
-click = ">=7.0,<8.0"
-click-didyoumean = ">=0.0.3"
-click-plugins = ">=1.1.1"
-click-repl = ">=0.1.6"
-Django = {version = ">=1.11", optional = true, markers = "extra == \"django\""}
-kombu = ">=5.1.0,<6.0"
-pytz = ">0.0-dev"
-redis = {version = ">=3.2.0", optional = true, markers = "extra == \"redis\""}
-vine = ">=5.0.0,<6.0"
-
-[package.extras]
-arangodb = ["pyArango (>=1.3.2)"]
-auth = ["cryptography"]
-azureblockblob = ["azure-storage-blob (==12.6.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.26.1)"]
-gevent = ["gevent (>=1.0.0)"]
-librabbitmq = ["librabbitmq (>=1.5.0)"]
-memcache = ["pylibmc"]
-mongodb = ["pymongo[srv] (>=3.3.0)"]
-msgpack = ["msgpack"]
-pymemcache = ["python-memcached"]
-pyro = ["pyro4"]
-pytest = ["pytest-celery"]
-redis = ["redis (>=3.2.0)"]
-s3 = ["boto3 (>=1.9.125)"]
-slmq = ["softlayer-messaging (>=1.0.3)"]
-solar = ["ephem"]
-sqlalchemy = ["sqlalchemy"]
-sqs = ["boto3 (>=1.9.125)", "pycurl (==7.43.0.5)"]
-tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"]
-yaml = ["PyYAML (>=3.10)"]
-zookeeper = ["kazoo (>=1.3.1)"]
-zstd = ["zstandard"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "celery-haystack-ng"
-version = "0.20.post2"
-description = "An app for integrating Celery with Haystack"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-celery = ">=4.0"
-django-appconf = ">=0.4.1"
-django-haystack = ">=2.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "celery-progress"
-version = "0.1.1"
-description = "Drop in, configurable, dependency-free progress bars for your Django/Celery applications."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.extras]
-rabbitmq = ["channels-rabbitmq"]
-redis = ["channels-redis"]
-websockets = ["channels"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "certifi"
-version = "2021.10.8"
-description = "Python package for providing Mozilla's CA Bundle."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "cffi"
-version = "1.15.0"
-description = "Foreign Function Interface for Python calling C code."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-pycparser = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "charset-normalizer"
-version = "2.0.8"
-description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
-category = "main"
-optional = false
-python-versions = ">=3.5.0"
-
-[package.extras]
-unicode_backport = ["unicodedata2"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "click"
-version = "7.1.2"
-description = "Composable command line interface toolkit"
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[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"
-
-[package.dependencies]
-click = ">=7"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[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 = "*"
-
-[package.dependencies]
-click = ">=4.0"
-
-[package.extras]
-dev = ["pytest (>=3.6)", "pytest-cov", "wheel", "coveralls"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "click-repl"
-version = "0.2.0"
-description = "REPL plugin for Click"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-click = "*"
-prompt-toolkit = "*"
-six = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "colorama"
-version = "0.4.4"
-description = "Cross-platform colored terminal text."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "colour"
-version = "0.1.5"
-description = "converts and manipulates various color representation (HSL, RVB, web, X11, ...)"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.extras]
-test = ["nose"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "configobj"
-version = "5.0.6"
-description = "Config file reading, writing and validation."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-six = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "coverage"
-version = "6.2"
-description = "Code coverage measurement for Python"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-tomli = {version = "*", optional = true, markers = "extra == \"toml\""}
-
-[package.extras]
-toml = ["tomli"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "cryptography"
-version = "36.0.0"
-description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-cffi = ">=1.12"
-
-[package.extras]
-docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"]
-docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"]
-pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"]
-sdist = ["setuptools_rust (>=0.11.4)"]
-ssh = ["bcrypt (>=3.1.5)"]
-test = ["pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "cssselect2"
-version = "0.4.1"
-description = "cssselect2"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-tinycss2 = "*"
-webencodings = "*"
-
-[package.extras]
-doc = ["sphinx", "sphinx-rtd-theme"]
-test = ["pytest", "pytest-cov", "pytest-flake8", "pytest-isort", "coverage"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "curlylint"
-version = "0.13.0"
-description = "{{ 🎀}} Experimental HTML templates linting for Jinja, Nunjucks, Django templates, Twig, Liquid"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-attrs = ">=17.2.0"
-click = ">=6.5"
-parsy = "1.1.0"
-pathspec = ">=0.6,<1"
-toml = ">=0.9.4"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "decorator"
-version = "5.1.0"
-description = "Decorators for Humans"
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[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.*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "deprecated"
-version = "1.2.13"
-description = "Python @deprecated decorator to deprecate old python classes, functions or methods."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-
-[package.dependencies]
-wrapt = ">=1.10,<2"
-
-[package.extras]
-dev = ["tox", "bump2version (<1)", "sphinx (<2)", "importlib-metadata (<3)", "importlib-resources (<4)", "configparser (<5)", "sphinxcontrib-websupport (<2)", "zipp (<2)", "PyTest (<5)", "PyTest-Cov (<2.6)", "pytest", "pytest-cov"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "dj-database-url"
-version = "0.5.0"
-description = "Use Database URLs in your Django Application."
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django"
-version = "3.2.9"
-description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-asgiref = ">=3.3.2,<4"
-pytz = "*"
-sqlparse = ">=0.2.2"
-
-[package.extras]
-argon2 = ["argon2-cffi (>=19.1.0)"]
-bcrypt = ["bcrypt"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-allauth"
-version = "0.46.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 = "*"
-
-[package.dependencies]
-Django = ">=2.0"
-pyjwt = {version = ">=1.7", extras = ["crypto"]}
-python3-openid = ">=3.0.8"
-requests = "*"
-requests-oauthlib = ">=0.3.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-any-js"
-version = "1.1"
-description = "Include JavaScript/CSS libraries with readable template tags"
-category = "main"
-optional = false
-python-versions = ">=3.7,<4.0"
-
-[package.dependencies]
-Django = ">=2.2,<4.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-appconf"
-version = "1.0.5"
-description = "A helper class for handling configuration defaults of packaged apps gracefully."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-django = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-auth-ldap"
-version = "3.0.0"
-description = "Django LDAP authentication backend."
-category = "main"
-optional = true
-python-versions = ">=3.6"
-
-[package.dependencies]
-Django = ">=2.2"
-python-ldap = ">=3.1"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-bleach"
-version = "1.0.0"
-description = "Easily use bleach with Django models and templates"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-bleach = ">=1.5.0"
-Django = ">=1.11"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-cachalot"
-version = "2.4.4"
-description = "Caches your Django ORM queries and automatically invalidates them."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-Django = ">=2.2,<3.3"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-cache-memoize"
-version = "0.1.10"
-description = "Django utility for a memoization decorator that uses the Django cache framework."
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.extras]
-dev = ["flake8", "tox", "twine", "therapist", "black"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-celery-beat"
-version = "2.2.1"
-description = "Database-backed Periodic Tasks."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-celery = ">=5.0,<6.0"
-Django = ">=2.2,<4.0"
-django-timezone-field = ">=4.1.0,<5.0"
-python-crontab = ">=2.3.4"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-celery-email"
-version = "3.0.0"
-description = "An async Django email backend using celery"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-celery = ">=4.0"
-django = ">=2.2"
-django-appconf = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-celery-results"
-version = "2.2.0"
-description = "Celery result backends for Django."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-celery = ">=5.0,<6.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-ckeditor"
-version = "6.2.0"
-description = "Django admin CKEditor integration."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-django-js-asset = ">=1.2.2"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-cleanup"
-version = "5.2.0"
-description = "Deletes old files."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-colorfield"
-version = "0.5.0"
-description = "simple color field for your models with a nice color-picker in the admin-interface."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-dbbackup"
-version = "3.3.0"
-description = "Management commands to help backup and restore a project database and media"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-Django = ">=1.5"
-pytz = "*"
-six = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-debug-toolbar"
-version = "3.2.2"
-description = "A configurable set of panels that display various debug information about the current request/response."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-Django = ">=2.2"
-sqlparse = ">=0.2.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-dynamic-preferences"
-version = "1.11.0"
-description = "Dynamic global and instance settings for your django project"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-django = ">=1.11"
-persisting-theory = ">=0.2.1"
-six = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-extensions"
-version = "3.1.5"
-description = "Extensions for Django"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-Django = ">=2.2"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-favicon-plus-reloaded"
-version = "1.1.3"
-description = "simple Django app which allows you to upload a image and it renders a wide variety for html link tags to display the favicon"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-django = "*"
-pillow = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-filter"
-version = "2.4.0"
-description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically."
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.dependencies]
-Django = ">=2.2"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-formtools"
-version = "2.3"
-description = "A set of high-level abstractions for Django forms"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-Django = ">=2.2"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-guardian"
-version = "2.4.0"
-description = "Implementation of per object permissions for Django."
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.dependencies]
-Django = ">=2.2"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-haystack"
-version = "3.1.1"
-description = "Pluggable search for Django."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-Django = ">=2.2"
-
-[package.extras]
-elasticsearch = ["elasticsearch (>=5,<6)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-health-check"
-version = "3.16.4"
-description = "Run checks on services like databases, queue servers, celery processes, etc."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-django = ">=2.2"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-image-cropping"
-version = "1.6.1"
-description = "A reusable app for cropping images easily and non-destructively in Django"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-django-appconf = ">=1.0.2"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-impersonate"
-version = "1.7.3"
-description = "Django app to allow superusers to impersonate other users."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-ipware"
-version = "4.0.0"
-description = "A Django application to retrieve user's IP address"
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-js-asset"
-version = "1.2.2"
-description = "script tag with additional attributes for django.forms.Media"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-js-reverse"
-version = "0.9.1"
-description = "Javascript url handling for Django that doesn't hurt."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-Django = ">=1.5"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-jsonstore"
-version = "0.5.0"
-description = "Expose JSONField data as a virtual django model fields."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-Django = ">=1.11"
-six = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-maintenance-mode"
-version = "0.16.1"
-description = "django-maintenance-mode shows a 503 error page when maintenance-mode is on."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-material"
-version = "1.10.0"
-description = "Material design for django forms and admin"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-six = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-menu-generator-ng"
-version = "1.2.3"
-description = "A straightforward menu generator for Django"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-model-utils"
-version = "4.2.0"
-description = "Django model mixins and utilities"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-Django = ">=2.0.1"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-oauth-toolkit"
-version = "1.5.0"
-description = "OAuth2 Provider for Django"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-django = ">=2.2"
-jwcrypto = ">=0.8.0"
-oauthlib = ">=3.1.0"
-requests = ">=2.13.0"
-six = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-otp"
-version = "1.1.3"
-description = "A pluggable framework for adding two-factor authentication to Django using one-time passwords."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-django = ">=2.2"
-
-[package.extras]
-qrcode = ["qrcode"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-otp-yubikey"
-version = "1.0.1"
-description = "A django-otp plugin that verifies YubiKey OTP tokens."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-django-otp = ">=1.0.0"
-YubiOTP = ">=0.2.2"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-phonenumber-field"
-version = "5.2.0"
-description = "An international phone number field for django models."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-Django = ">=2.2"
-phonenumbers = {version = ">=7.0.2", optional = true, markers = "extra == \"phonenumbers\""}
-
-[package.extras]
-phonenumbers = ["phonenumbers (>=7.0.2)"]
-phonenumberslite = ["phonenumberslite (>=7.0.2)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-polymorphic"
-version = "3.1.0"
-description = "Seamless polymorphic inheritance for Django models"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-Django = ">=2.1"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-prometheus"
-version = "2.1.0"
-description = "Django middlewares to monitor your application with Prometheus.io."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-prometheus-client = ">=0.7"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-redis"
-version = "5.1.0"
-description = "Full featured redis cache backend for Django."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-Django = ">=2.2"
-redis = ">=3,<4"
-
-[package.extras]
-hiredis = ["redis[hiredis] (>=3,<4)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-render-block"
-version = "0.8.1"
-description = "Render a particular block from a template to a string."
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.dependencies]
-django = ">=2.2"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-reversion"
-version = "4.0.1"
-description = "An extension to the Django web framework that provides version control for model instances."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-django = ">=2.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-sass-processor"
-version = "1.0.0"
-description = "SASS processor to compile SCSS files into *.css, while rendering, or offline."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.extras]
-management_command = ["django-compressor (>=2.4)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-select2"
-version = "7.9.0"
-description = "Select2 option fields for Django"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-django = ">=2.2"
-django-appconf = ">=0.6.0"
-
-[package.extras]
-test = ["pytest", "pytest-cov", "pytest-django", "selenium"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-storages"
-version = "1.12.3"
-description = "Support for many storage backends in Django"
-category = "main"
-optional = true
-python-versions = ">=3.5"
-
-[package.dependencies]
-Django = ">=2.2"
-
-[package.extras]
-azure = ["azure-storage-blob (>=12.0.0)"]
-boto3 = ["boto3 (>=1.4.4)"]
-dropbox = ["dropbox (>=7.2.1)"]
-google = ["google-cloud-storage (>=1.27.0)"]
-libcloud = ["apache-libcloud"]
-sftp = ["paramiko"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-stubs"
-version = "1.9.0"
-description = "Mypy stubs for Django"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-django = "*"
-django-stubs-ext = ">=0.3.0"
-mypy = ">=0.910"
-toml = "*"
-types-pytz = "*"
-types-PyYAML = "*"
-typing-extensions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-stubs-ext"
-version = "0.3.1"
-description = "Monkey-patching and extensions for django-stubs"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-django = "*"
-typing-extensions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-tables2"
-version = "2.4.1"
-description = "Table/data-grid framework for Django"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-Django = ">=1.11"
-
-[package.extras]
-tablib = ["tablib"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-templated-email"
-version = "3.0.0"
-description = "A Django oriented templated / transaction email abstraction"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-django-render-block = ">=0.5"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-timezone-field"
-version = "4.2.1"
-description = "A Django app providing database and form fields for pytz timezone objects."
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.dependencies]
-django = ">=2.2"
-pytz = "*"
-
-[package.extras]
-rest_framework = ["djangorestframework (>=3.0.0)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-titofisto"
-version = "0.2.0"
-description = "Django Time-Token File Storage"
-category = "main"
-optional = false
-python-versions = ">=3.9,<4.0"
-
-[package.dependencies]
-Django = ">2.2,<4.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-two-factor-auth"
-version = "1.13.1"
-description = "Complete Two-Factor Authentication for Django"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-Django = ">=2.2"
-django-formtools = "*"
-django-otp = ">=0.8.0"
-django-otp-yubikey = {version = "*", optional = true, markers = "extra == \"yubikey\""}
-django-phonenumber-field = ">=1.1.0,<6"
-phonenumbers = {version = ">=7.0.9,<8.99", optional = true, markers = "extra == \"phonenumbers\""}
-qrcode = ">=4.0.0,<6.99"
-twilio = {version = ">=6.0", optional = true, markers = "extra == \"call\""}
-
-[package.extras]
-call = ["twilio (>=6.0)"]
-phonenumbers = ["phonenumbers (>=7.0.9,<8.99)"]
-phonenumberslite = ["phonenumberslite (>=7.0.9,<8.99)"]
-sms = ["twilio (>=6.0)"]
-yubikey = ["django-otp-yubikey"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-uwsgi-ng"
-version = "1.1.2"
-description = "uWSGI stuff for Django projects"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.extras]
-uwsgi = ["uwsgi"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-widget-tweaks"
-version = "1.4.9"
-description = "Tweak the form field rendering in templates, not in python-level form definitions."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "django-yarnpkg"
-version = "6.0.1"
-description = "Integrate django with yarnpkg"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-django = "*"
-six = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "djangorestframework"
-version = "3.12.4"
-description = "Web APIs for Django, made easy."
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.dependencies]
-django = ">=2.2"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "docutils"
-version = "0.16"
-description = "Docutils -- Python Documentation Utilities"
-category = "dev"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "dparse"
-version = "0.5.1"
-description = "A parser for Python dependency files"
-category = "dev"
-optional = false
-python-versions = ">=3.5"
-
-[package.dependencies]
-packaging = "*"
-pyyaml = "*"
-toml = "*"
-
-[package.extras]
-pipenv = ["pipenv"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "dynaconf"
-version = "3.1.7"
-description = "The dynamic configurator for your Python Project"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-configobj = {version = "*", optional = true, markers = "extra == \"ini\""}
-"ruamel.yaml" = {version = "*", optional = true, markers = "extra == \"yaml\""}
-toml = {version = "*", optional = true, markers = "extra == \"toml\""}
-
-[package.extras]
-all = ["redis", "ruamel.yaml", "configobj", "hvac"]
-configobj = ["configobj"]
-ini = ["configobj"]
-redis = ["redis"]
-toml = ["toml"]
-vault = ["hvac"]
-yaml = ["ruamel.yaml"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "easy-thumbnails"
-version = "2.8"
-description = "Easy thumbnails for Django"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-django = ">=2.2,<4.0"
-pillow = "*"
-reportlab = "*"
-svglib = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "flake8"
-version = "4.0.1"
-description = "the modular source code checker: pep8 pyflakes and co"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-mccabe = ">=0.6.0,<0.7.0"
-pycodestyle = ">=2.8.0,<2.9.0"
-pyflakes = ">=2.4.0,<2.5.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "flake8-bandit"
-version = "2.1.2"
-description = "Automated security testing with bandit and flake8."
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-bandit = "*"
-flake8 = "*"
-flake8-polyfill = "*"
-pycodestyle = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "flake8-black"
-version = "0.2.3"
-description = "flake8 plugin to call black as a code style validator"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-black = "*"
-flake8 = ">=3.0.0"
-toml = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "flake8-builtins"
-version = "1.5.3"
-description = "Check for python builtins being used as variables or parameters."
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-flake8 = "*"
-
-[package.extras]
-test = ["coverage", "coveralls", "mock", "pytest", "pytest-cov"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "flake8-django"
-version = "1.1.1"
-description = "Plugin to catch bad style specific to Django Projects"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-flake8 = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "flake8-docstrings"
-version = "1.6.0"
-description = "Extension for flake8 which uses pydocstyle to check docstrings"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-flake8 = ">=3"
-pydocstyle = ">=2.1"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "flake8-fixme"
-version = "1.1.1"
-description = "Check for FIXME, TODO and other temporary developer notes. Plugin for flake8."
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "flake8-isort"
-version = "4.1.1"
-description = "flake8 plugin that integrates isort ."
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-flake8 = ">=3.2.1,<5"
-isort = ">=4.3.5,<6"
-testfixtures = ">=6.8.0,<7"
-
-[package.extras]
-test = ["pytest-cov"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "flake8-mypy"
-version = "17.8.0"
-description = "A plugin for flake8 integrating mypy."
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-attrs = "*"
-flake8 = ">=3.0.0"
-mypy = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "flake8-polyfill"
-version = "1.0.2"
-description = "Polyfill package for Flake8 plugins"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-flake8 = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "flake8-rst-docstrings"
-version = "0.2.3"
-description = "Python docstring reStructuredText (RST) validator"
-category = "dev"
-optional = false
-python-versions = ">=3.3"
-
-[package.dependencies]
-flake8 = ">=3.0.0"
-pygments = "*"
-restructuredtext-lint = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "freezegun"
-version = "1.1.0"
-description = "Let your Python tests travel through time"
-category = "dev"
-optional = false
-python-versions = ">=3.5"
-
-[package.dependencies]
-python-dateutil = ">=2.7"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "gitdb"
-version = "4.0.9"
-description = "Git Object Database"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-smmap = ">=3.0.1,<6"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "gitpython"
-version = "3.1.24"
-description = "GitPython is a python library used to interact with Git repositories"
-category = "dev"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-gitdb = ">=4.0.1,<5"
-typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.10\""}
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "h11"
-version = "0.12.0"
-description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "haystack-redis"
-version = "0.0.1"
-description = "Use redis as a persistence layer for Whoosh and Haystack"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-django-haystack = "*"
-redis = "*"
-whoosh = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "html2text"
-version = "2020.1.16"
-description = "Turn HTML into equivalent Markdown-structured text."
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "idna"
-version = "3.3"
-description = "Internationalized Domain Names in Applications (IDNA)"
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "imagesize"
-version = "1.3.0"
-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.*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "iniconfig"
-version = "1.1.1"
-description = "iniconfig: brain-dead simple config-ini parsing"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "ipython"
-version = "7.30.0"
-description = "IPython: Productive Interactive Computing"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-appnope = {version = "*", markers = "sys_platform == \"darwin\""}
-backcall = "*"
-colorama = {version = "*", markers = "sys_platform == \"win32\""}
-decorator = "*"
-jedi = ">=0.16"
-matplotlib-inline = "*"
-pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""}
-pickleshare = "*"
-prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0"
-pygments = "*"
-traitlets = ">=4.2"
-
-[package.extras]
-all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"]
-doc = ["Sphinx (>=1.3)"]
-kernel = ["ipykernel"]
-nbconvert = ["nbconvert"]
-nbformat = ["nbformat"]
-notebook = ["notebook", "ipywidgets"]
-parallel = ["ipyparallel"]
-qtconsole = ["qtconsole"]
-test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "isort"
-version = "5.10.1"
-description = "A Python utility / library to sort Python imports."
-category = "dev"
-optional = false
-python-versions = ">=3.6.1,<4.0"
-
-[package.extras]
-colors = ["colorama (>=0.4.3,<0.5.0)"]
-requirements_deprecated_finder = ["pip-api", "pipreqs"]
-pipfile_deprecated_finder = ["pipreqs", "requirementslib"]
-plugins = ["setuptools"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "jedi"
-version = "0.18.1"
-description = "An autocompletion tool for Python that can be used for text editors."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-parso = ">=0.8.0,<0.9.0"
-
-[package.extras]
-qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
-testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "jinja2"
-version = "3.0.3"
-description = "A very fast and expressive template engine."
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-MarkupSafe = ">=2.0"
-
-[package.extras]
-i18n = ["Babel (>=2.7)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "jmespath"
-version = "0.10.0"
-description = "JSON Matching Expressions"
-category = "main"
-optional = true
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "jwcrypto"
-version = "1.0"
-description = "Implementation of JOSE Web standards"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-cryptography = ">=2.3"
-deprecated = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "kombu"
-version = "5.2.2"
-description = "Messaging library for Python."
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.dependencies]
-amqp = ">=5.0.6,<6.0.0"
-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)"]
-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.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "libsass"
-version = "0.21.0"
-description = "Sass for Python: A straightforward binding of libsass for Python."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-six = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "license-expression"
-version = "21.6.14"
-description = "license-expression is a comprehensive utility library to parse, compare, simplify and normalize license expressions (such as SPDX license expressions) using boolean logic."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-"boolean.py" = ">=3.6,<4.0.0"
-
-[package.extras]
-docs = ["Sphinx (>=3.3.1)", "sphinx-rtd-theme (>=0.5.0)", "doc8 (>=0.8.1)"]
-testing = ["pytest (>=6)", "pytest-xdist (>=2)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "lxml"
-version = "4.6.4"
-description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*"
-
-[package.extras]
-cssselect = ["cssselect (>=0.7)"]
-html5 = ["html5lib"]
-htmlsoup = ["beautifulsoup4"]
-source = ["Cython (>=0.29.7)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "markupsafe"
-version = "2.0.1"
-description = "Safely add untrusted strings to HTML/XML markup."
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "matplotlib-inline"
-version = "0.1.3"
-description = "Inline Matplotlib backend for Jupyter"
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.dependencies]
-traitlets = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "mccabe"
-version = "0.6.1"
-description = "McCabe checker, plugin for flake8"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "mypy"
-version = "0.910"
-description = "Optional static typing for Python"
-category = "dev"
-optional = false
-python-versions = ">=3.5"
-
-[package.dependencies]
-mypy-extensions = ">=0.4.3,<0.5.0"
-toml = "*"
-typing-extensions = ">=3.7.4"
-
-[package.extras]
-dmypy = ["psutil (>=4.0)"]
-python2 = ["typed-ast (>=1.4.0,<1.5.0)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "mypy-extensions"
-version = "0.4.3"
-description = "Experimental type system extensions for programs checked with the mypy typechecker."
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "oauthlib"
-version = "3.1.1"
-description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.extras]
-rsa = ["cryptography (>=3.0.0,<4)"]
-signals = ["blinker (>=1.4.0)"]
-signedtoken = ["cryptography (>=3.0.0,<4)", "pyjwt (>=2.0.0,<3)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "outcome"
-version = "1.1.0"
-description = "Capture the outcome of Python function calls."
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-attrs = ">=19.2.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "packaging"
-version = "21.3"
-description = "Core utilities for Python packages"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "parso"
-version = "0.8.3"
-description = "A Python Parser"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.extras]
-qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
-testing = ["docopt", "pytest (<6.0.0)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "parsy"
-version = "1.1.0"
-description = "easy-to-use parser combinators, for parsing in pure Python"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pathspec"
-version = "0.9.0"
-description = "Utility library for gitignore style pattern matching of file paths."
-category = "dev"
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pbr"
-version = "5.8.0"
-description = "Python Build Reasonableness"
-category = "dev"
-optional = false
-python-versions = ">=2.6"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "persisting-theory"
-version = "0.2.1"
-description = "Registries that can autodiscover values accross your project apps"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pexpect"
-version = "4.8.0"
-description = "Pexpect allows easy control of interactive console applications."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-ptyprocess = ">=0.5"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pg8000"
-version = "1.23.0"
-description = "PostgreSQL interface library"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-scramp = ">=1.4.1"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "phonenumbers"
-version = "8.12.38"
-description = "Python version of Google's common library for parsing, formatting, storing and validating international phone numbers."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pickleshare"
-version = "0.7.5"
-description = "Tiny 'shelve'-like database with concurrency support"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pillow"
-version = "8.4.0"
-description = "Python Imaging Library (Fork)"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "platformdirs"
-version = "2.4.0"
-description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.extras]
-docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"]
-test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pluggy"
-version = "1.0.0"
-description = "plugin and hook calling mechanisms for python"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.extras]
-dev = ["pre-commit", "tox"]
-testing = ["pytest", "pytest-benchmark"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "prometheus-client"
-version = "0.12.0"
-description = "Python client for the Prometheus monitoring system."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-
-[package.extras]
-twisted = ["twisted"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "prompt-toolkit"
-version = "3.0.23"
-description = "Library for building powerful interactive command lines in Python"
-category = "main"
-optional = false
-python-versions = ">=3.6.2"
-
-[package.dependencies]
-wcwidth = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "psutil"
-version = "5.8.0"
-description = "Cross-platform lib for process and system monitoring in Python."
-category = "main"
-optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-
-[package.extras]
-test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "psycopg2"
-version = "2.9.2"
-description = "psycopg2 - Python-PostgreSQL Database Adapter"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "ptyprocess"
-version = "0.7.0"
-description = "Run a subprocess in a pseudo terminal"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "py"
-version = "1.11.0"
-description = "library with cross-python path, ini-parsing, io, code, log facilities"
-category = "dev"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pyasn1"
-version = "0.4.8"
-description = "ASN.1 types and codecs"
-category = "main"
-optional = true
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pyasn1-modules"
-version = "0.2.8"
-description = "A collection of ASN.1-based protocols modules."
-category = "main"
-optional = true
-python-versions = "*"
-
-[package.dependencies]
-pyasn1 = ">=0.4.6,<0.5.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pycodestyle"
-version = "2.8.0"
-description = "Python style guide checker"
-category = "dev"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[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.*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pycryptodome"
-version = "3.11.0"
-description = "Cryptographic library for Python"
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pydocstyle"
-version = "6.1.1"
-description = "Python docstring style checker"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-snowballstemmer = "*"
-
-[package.extras]
-toml = ["toml"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pyflakes"
-version = "2.4.0"
-description = "passive checker of Python programs"
-category = "dev"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pygments"
-version = "2.10.0"
-description = "Pygments is a syntax highlighting package written in Python."
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pyjwt"
-version = "2.3.0"
-description = "JSON Web Token implementation in Python"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-cryptography = {version = ">=3.3.1", optional = true, markers = "extra == \"crypto\""}
-
-[package.extras]
-crypto = ["cryptography (>=3.3.1)"]
-dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1)", "pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)", "mypy", "pre-commit"]
-docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
-tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pyopenssl"
-version = "21.0.0"
-description = "Python wrapper module around the OpenSSL library"
-category = "dev"
-optional = false
-python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*"
-
-[package.dependencies]
-cryptography = ">=3.3"
-six = ">=1.5.2"
-
-[package.extras]
-docs = ["sphinx", "sphinx-rtd-theme"]
-test = ["flaky", "pretend", "pytest (>=3.0.1)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pyparsing"
-version = "3.0.6"
-description = "Python parsing module"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.extras]
-diagrams = ["jinja2", "railroad-diagrams"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pytest"
-version = "6.2.5"
-description = "pytest: simple powerful testing with Python"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""}
-attrs = ">=19.2.0"
-colorama = {version = "*", markers = "sys_platform == \"win32\""}
-iniconfig = "*"
-packaging = "*"
-pluggy = ">=0.12,<2.0"
-py = ">=1.8.2"
-toml = "*"
-
-[package.extras]
-testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pytest-cov"
-version = "3.0.0"
-description = "Pytest plugin for measuring coverage."
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-coverage = {version = ">=5.2.1", extras = ["toml"]}
-pytest = ">=4.6"
-
-[package.extras]
-testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pytest-django"
-version = "4.5.0"
-description = "A Django plugin for pytest."
-category = "dev"
-optional = false
-python-versions = ">=3.5"
-
-[package.dependencies]
-pytest = ">=5.4.0"
-
-[package.extras]
-docs = ["sphinx", "sphinx-rtd-theme"]
-testing = ["django", "django-configurations (>=2.0)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pytest-django-testing-postgresql"
-version = "0.1.post0"
-description = "Use a temporary PostgreSQL database with pytest-django"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-dj-database-url = "*"
-"testing.postgresql" = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pytest-sugar"
-version = "0.9.4"
-description = "pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly)."
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-packaging = ">=14.1"
-pytest = ">=2.9"
-termcolor = ">=1.1.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "python-crontab"
-version = "2.6.0"
-description = "Python Crontab API"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-python-dateutil = "*"
-
-[package.extras]
-cron-description = ["cron-descriptor"]
-cron-schedule = ["croniter"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "python-dateutil"
-version = "2.8.2"
-description = "Extensions to the standard Python datetime module"
-category = "main"
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
-
-[package.dependencies]
-six = ">=1.5"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "python-gnupg"
-version = "0.4.8"
-description = "A wrapper for the Gnu Privacy Guard (GPG or GnuPG)"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "python-ldap"
-version = "3.4.0"
-description = "Python modules for implementing LDAP clients"
-category = "main"
-optional = true
-python-versions = ">=3.6"
-
-[package.dependencies]
-pyasn1 = ">=0.3.7"
-pyasn1_modules = ">=0.1.5"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "python3-openid"
-version = "3.2.0"
-description = "OpenID support for modern servers and consumers."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-defusedxml = "*"
-
-[package.extras]
-mysql = ["mysql-connector-python"]
-postgresql = ["psycopg2"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pytz"
-version = "2021.3"
-description = "World timezone definitions, modern and historical"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "pyyaml"
-version = "6.0"
-description = "YAML parser and emitter for Python"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "qrcode"
-version = "6.1"
-description = "QR Code image generator"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-colorama = {version = "*", markers = "platform_system == \"Windows\""}
-six = "*"
-
-[package.extras]
-dev = ["tox", "pytest", "mock"]
-maintainer = ["zest.releaser"]
-pil = ["pillow"]
-test = ["pytest", "pytest-cov", "mock"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "redis"
-version = "3.5.3"
-description = "Python client for Redis key-value store"
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-
-[package.extras]
-hiredis = ["hiredis (>=0.1.3)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "regex"
-version = "2021.11.10"
-description = "Alternative regular expression module, to replace re."
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "reportlab"
-version = "3.6.3"
-description = "The Reportlab Toolkit"
-category = "main"
-optional = false
-python-versions = ">=3.6, <4"
-
-[package.dependencies]
-pillow = ">=4.0.0"
-
-[package.extras]
-RLPYCAIRO = ["rlPyCairo (>=0.0.5)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "requests"
-version = "2.26.0"
-description = "Python HTTP for Humans."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
-
-[package.dependencies]
-certifi = ">=2017.4.17"
-charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
-idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
-urllib3 = ">=1.21.1,<1.27"
-
-[package.extras]
-socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
-use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "requests-oauthlib"
-version = "1.3.0"
-description = "OAuthlib authentication support for Requests."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-
-[package.dependencies]
-oauthlib = ">=3.0.0"
-requests = ">=2.0.0"
-
-[package.extras]
-rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "restructuredtext-lint"
-version = "1.3.2"
-description = "reStructuredText linter"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-docutils = ">=0.11,<1.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "ruamel.yaml"
-version = "0.17.17"
-description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order"
-category = "main"
-optional = false
-python-versions = ">=3"
-
-[package.dependencies]
-"ruamel.yaml.clib" = {version = ">=0.1.2", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.10\""}
-
-[package.extras]
-docs = ["ryd"]
-jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "ruamel.yaml.clib"
-version = "0.2.6"
-description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml"
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "rules"
-version = "3.0"
-description = "Awesome Django authorization, without the database"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "s3transfer"
-version = "0.5.0"
-description = "An Amazon S3 Transfer Manager"
-category = "main"
-optional = true
-python-versions = ">= 3.6"
-
-[package.dependencies]
-botocore = ">=1.12.36,<2.0a.0"
-
-[package.extras]
-crt = ["botocore[crt] (>=1.20.29,<2.0a.0)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "safety"
-version = "1.10.3"
-description = "Checks installed dependencies for known vulnerabilities."
-category = "dev"
-optional = false
-python-versions = ">=3.5"
-
-[package.dependencies]
-Click = ">=6.0"
-dparse = ">=0.5.1"
-packaging = "*"
-requests = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "scramp"
-version = "1.4.1"
-description = "An implementation of the SCRAM protocol."
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-asn1crypto = ">=1.4.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "selenium"
-version = "4.1.0"
-description = ""
-category = "dev"
-optional = false
-python-versions = "~=3.7"
-
-[package.dependencies]
-trio = ">=0.17,<1.0"
-trio-websocket = ">=0.9,<1.0"
-urllib3 = {version = ">=1.26,<2.0", extras = ["secure"]}
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "sentry-sdk"
-version = "1.5.0"
-description = "Python client for Sentry (https://sentry.io)"
-category = "main"
-optional = true
-python-versions = "*"
-
-[package.dependencies]
-certifi = "*"
-urllib3 = ">=1.10.0"
-
-[package.extras]
-aiohttp = ["aiohttp (>=3.5)"]
-beam = ["apache-beam (>=2.12)"]
-bottle = ["bottle (>=0.12.13)"]
-celery = ["celery (>=3)"]
-chalice = ["chalice (>=1.16.0)"]
-django = ["django (>=1.8)"]
-falcon = ["falcon (>=1.4)"]
-flask = ["flask (>=0.11)", "blinker (>=1.1)"]
-httpx = ["httpx (>=0.16.0)"]
-pure_eval = ["pure-eval", "executing", "asttokens"]
-pyspark = ["pyspark (>=2.4.4)"]
-rq = ["rq (>=0.6)"]
-sanic = ["sanic (>=0.8)"]
-sqlalchemy = ["sqlalchemy (>=1.2)"]
-tornado = ["tornado (>=5)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[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.*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "smmap"
-version = "5.0.0"
-description = "A pure Python implementation of a sliding window memory map manager"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "sniffio"
-version = "1.2.0"
-description = "Sniff out which async library your code is running under"
-category = "dev"
-optional = false
-python-versions = ">=3.5"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[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 = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "sortedcontainers"
-version = "2.4.0"
-description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "soupsieve"
-version = "2.3.1"
-description = "A modern CSS selector implementation for Beautiful Soup."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "spdx-license-list"
-version = "0.5.2"
-description = "A simple tool/library for working with SPDX license definitions."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "sphinx"
-version = "3.5.4"
-description = "Python documentation generator"
-category = "dev"
-optional = false
-python-versions = ">=3.5"
-
-[package.dependencies]
-alabaster = ">=0.7,<0.8"
-babel = ">=1.3"
-colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""}
-docutils = ">=0.12,<0.17"
-imagesize = "*"
-Jinja2 = ">=2.3"
-packaging = "*"
-Pygments = ">=2.0"
-requests = ">=2.5.0"
-snowballstemmer = ">=1.1"
-sphinxcontrib-applehelp = "*"
-sphinxcontrib-devhelp = "*"
-sphinxcontrib-htmlhelp = "*"
-sphinxcontrib-jsmath = "*"
-sphinxcontrib-qthelp = "*"
-sphinxcontrib-serializinghtml = "*"
-
-[package.extras]
-docs = ["sphinxcontrib-websupport"]
-lint = ["flake8 (>=3.5.0)", "isort", "mypy (>=0.800)", "docutils-stubs"]
-test = ["pytest", "pytest-cov", "html5lib", "cython", "typed-ast"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "sphinx-autodoc-typehints"
-version = "1.12.0"
-description = "Type hints (PEP 484) support for the Sphinx autodoc extension"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-Sphinx = ">=3.0"
-
-[package.extras]
-test = ["pytest (>=3.1.0)", "typing-extensions (>=3.5)", "sphobjinv (>=2.0)", "Sphinx (>=3.2.0)", "dataclasses"]
-type_comments = ["typed-ast (>=1.4.0)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "sphinx-materialdesign-theme"
-version = "0.1.11"
-description = "Sphinx Material Design Theme"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[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"
-
-[package.extras]
-lint = ["flake8", "mypy", "docutils-stubs"]
-test = ["pytest"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[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"
-
-[package.extras]
-lint = ["flake8", "mypy", "docutils-stubs"]
-test = ["pytest"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "sphinxcontrib-django"
-version = "0.5.1"
-description = "Improve the Sphinx autodoc for Django classes."
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[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"
-
-[package.extras]
-lint = ["flake8", "mypy", "docutils-stubs"]
-test = ["pytest", "html5lib"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[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"
-
-[package.extras]
-test = ["pytest", "flake8", "mypy"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[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"
-
-[package.extras]
-lint = ["flake8", "mypy", "docutils-stubs"]
-test = ["pytest"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[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"
-
-[package.extras]
-lint = ["flake8", "mypy", "docutils-stubs"]
-test = ["pytest"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "sqlparse"
-version = "0.4.2"
-description = "A non-validating SQL parser."
-category = "main"
-optional = false
-python-versions = ">=3.5"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "stevedore"
-version = "3.5.0"
-description = "Manage dynamic plugins for Python applications"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-pbr = ">=2.0.0,<2.1.0 || >2.1.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "svglib"
-version = "1.1.0"
-description = "A pure-Python library for reading and converting SVG"
-category = "main"
-optional = false
-python-versions = ">=3"
-
-[package.dependencies]
-cssselect2 = ">=0.2.0"
-lxml = "*"
-reportlab = "*"
-tinycss2 = ">=0.6.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "termcolor"
-version = "1.1.0"
-description = "ANSII Color formatting for output in terminal."
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "testfixtures"
-version = "6.18.3"
-description = "A collection of helpers and mock objects for unit tests and doc tests."
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.extras]
-build = ["setuptools-git", "wheel", "twine"]
-docs = ["sphinx", "zope.component", "sybil", "twisted", "mock", "django (<2)", "django"]
-test = ["pytest (>=3.6)", "pytest-cov", "pytest-django", "zope.component", "sybil", "twisted", "mock", "django (<2)", "django"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "testing.common.database"
-version = "2.0.3"
-description = "utilities for testing.* packages"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.extras]
-testing = ["nose"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "testing.postgresql"
-version = "1.3.0"
-description = "automatically setups a postgresql instance in a temporary directory, and destroys it after testing"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-pg8000 = ">=1.10"
-"testing.common.database" = "*"
-
-[package.extras]
-testing = ["sqlalchemy", "nose", "psycopg2"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "tinycss2"
-version = "1.1.1"
-description = "A tiny CSS parser"
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-webencodings = ">=0.4"
-
-[package.extras]
-doc = ["sphinx", "sphinx-rtd-theme"]
-test = ["pytest", "pytest-cov", "pytest-flake8", "pytest-isort", "coverage"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "toml"
-version = "0.10.2"
-description = "Python Library for Tom's Obvious, Minimal Language"
-category = "main"
-optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "tomli"
-version = "1.2.2"
-description = "A lil' TOML parser"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "traitlets"
-version = "5.1.1"
-description = "Traitlets Python configuration system"
-category = "main"
-optional = false
-python-versions = ">=3.7"
-
-[package.extras]
-test = ["pytest"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "trio"
-version = "0.19.0"
-description = "A friendly Python library for async concurrency and I/O"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.dependencies]
-async-generator = ">=1.9"
-attrs = ">=19.2.0"
-cffi = {version = ">=1.14", markers = "os_name == \"nt\" and implementation_name != \"pypy\""}
-idna = "*"
-outcome = "*"
-sniffio = "*"
-sortedcontainers = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "trio-websocket"
-version = "0.9.2"
-description = "WebSocket library for Trio"
-category = "dev"
-optional = false
-python-versions = ">=3.5"
-
-[package.dependencies]
-async-generator = ">=1.10"
-trio = ">=0.11"
-wsproto = ">=0.14"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "twilio"
-version = "7.3.2"
-description = "Twilio API client and TwiML generator"
-category = "main"
-optional = false
-python-versions = ">=3.6.0"
-
-[package.dependencies]
-PyJWT = ">=2.0.0,<3.0.0"
-pytz = "*"
-requests = ">=2.0.0"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "types-pytz"
-version = "2021.3.1"
-description = "Typing stubs for pytz"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "types-pyyaml"
-version = "6.0.1"
-description = "Typing stubs for PyYAML"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "typing-extensions"
-version = "4.0.1"
-description = "Backported and Experimental Type Hints for Python 3.6+"
-category = "dev"
-optional = false
-python-versions = ">=3.6"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "urllib3"
-version = "1.26.7"
-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.*, <4"
-
-[package.dependencies]
-certifi = {version = "*", optional = true, markers = "extra == \"secure\""}
-cryptography = {version = ">=1.3.4", optional = true, markers = "extra == \"secure\""}
-idna = {version = ">=2.0.0", optional = true, markers = "extra == \"secure\""}
-pyOpenSSL = {version = ">=0.14", optional = true, markers = "extra == \"secure\""}
-
-[package.extras]
-brotli = ["brotlipy (>=0.6.0)"]
-secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
-socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "uwsgi"
-version = "2.0.20"
-description = "The uWSGI server"
-category = "dev"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "vine"
-version = "5.0.0"
-description = "Promises, promises, promises."
-category = "main"
-optional = false
-python-versions = ">=3.6"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "wcwidth"
-version = "0.2.5"
-description = "Measures the displayed width of unicode strings in a terminal"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "webencodings"
-version = "0.5.1"
-description = "Character encoding aliases for legacy web content"
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "whoosh"
-version = "2.7.4"
-description = "Fast, pure-Python full text indexing, search, and spell checking library."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "wrapt"
-version = "1.13.3"
-description = "Module for decorators, wrappers and monkey patching."
-category = "main"
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "wsproto"
-version = "1.0.0"
-description = "WebSockets state-machine based protocol implementation"
-category = "dev"
-optional = false
-python-versions = ">=3.6.1"
-
-[package.dependencies]
-h11 = ">=0.9.0,<1"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[[package]]
-name = "yubiotp"
-version = "1.0.0.post1"
-description = "A library for verifying YubiKey OTP tokens, both locally and through a Yubico web service."
-category = "main"
-optional = false
-python-versions = "*"
-
-[package.dependencies]
-pycryptodome = "*"
-
-[package.source]
-type = "legacy"
-url = "https://edugit.org/api/v4/projects/461/packages/pypi/simple"
-reference = "gitlab"
-
-[extras]
-ldap = ["django-auth-ldap"]
-s3 = ["boto3", "django-storages"]
-sentry = []
-
-[metadata]
-lock-version = "1.1"
-python-versions = "^3.9"
-content-hash = "37b5a1b20cbd6d2770c44fe0287022c3278081c7da4f0c08b5f97982d78ead4c"
-
-[metadata.files]
-alabaster = [
-    {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"},
-    {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
-]
-aleksis-builddeps = [
-    {file = "AlekSIS-Builddeps-5+20211130152626.23302bab.tar.gz", hash = "sha256:bed0e9a4d00cf45f8cd75fcf059402c4b81525785ba2cb3d89c4cd6df907b58c"},
-    {file = "AlekSIS_Builddeps-5+20211130152626.23302bab-py3-none-any.whl", hash = "sha256:023f594eb06667f747059c8d69a2058e195d4644644a09d747197f152ece9741"},
-]
-amqp = [
-    {file = "amqp-5.0.6-py3-none-any.whl", hash = "sha256:493a2ac6788ce270a2f6a765b017299f60c1998f5a8617908ee9be082f7300fb"},
-    {file = "amqp-5.0.6.tar.gz", hash = "sha256:03e16e94f2b34c31f8bf1206d8ddd3ccaa4c315f7f6a1879b7b1210d229568c2"},
-]
-appnope = [
-    {file = "appnope-0.1.2-py2.py3-none-any.whl", hash = "sha256:93aa393e9d6c54c5cd570ccadd8edad61ea0c4b9ea7a01409020c9aa019eb442"},
-    {file = "appnope-0.1.2.tar.gz", hash = "sha256:dd83cd4b5b460958838f6eb3000c660b1f9caf2a5b1de4264e941512f603258a"},
-]
-asgiref = [
-    {file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"},
-    {file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"},
-]
-asn1crypto = [
-    {file = "asn1crypto-1.4.0-py2.py3-none-any.whl", hash = "sha256:4bcdf33c861c7d40bdcd74d8e4dd7661aac320fcdf40b9a3f95b4ee12fde2fa8"},
-    {file = "asn1crypto-1.4.0.tar.gz", hash = "sha256:f4f6e119474e58e04a2b1af817eb585b4fd72bdd89b998624712b5c99be7641c"},
-]
-async-generator = [
-    {file = "async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b"},
-    {file = "async_generator-1.10.tar.gz", hash = "sha256:6ebb3d106c12920aaae42ccb6f787ef5eefdcdd166ea3d628fa8476abe712144"},
-]
-atomicwrites = [
-    {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
-    {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
-]
-attrs = [
-    {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
-    {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
-]
-babel = [
-    {file = "Babel-2.9.1-py2.py3-none-any.whl", hash = "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9"},
-    {file = "Babel-2.9.1.tar.gz", hash = "sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0"},
-]
-backcall = [
-    {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
-    {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
-]
-bandit = [
-    {file = "bandit-1.7.1-py3-none-any.whl", hash = "sha256:f5acd838e59c038a159b5c621cf0f8270b279e884eadd7b782d7491c02add0d4"},
-    {file = "bandit-1.7.1.tar.gz", hash = "sha256:a81b00b5436e6880fa8ad6799bc830e02032047713cbb143a12939ac67eb756c"},
-]
-beautifulsoup4 = [
-    {file = "beautifulsoup4-4.10.0-py3-none-any.whl", hash = "sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf"},
-    {file = "beautifulsoup4-4.10.0.tar.gz", hash = "sha256:c23ad23c521d818955a4151a67d81580319d4bf548d3d49f4223ae041ff98891"},
-]
-billiard = [
-    {file = "billiard-3.6.4.0-py3-none-any.whl", hash = "sha256:87103ea78fa6ab4d5c751c4909bcff74617d985de7fa8b672cf8618afd5a875b"},
-    {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"},
-]
-black = [
-    {file = "black-21.11b1-py3-none-any.whl", hash = "sha256:802c6c30b637b28645b7fde282ed2569c0cd777dbe493a41b6a03c1d903f99ac"},
-    {file = "black-21.11b1.tar.gz", hash = "sha256:a042adbb18b3262faad5aff4e834ff186bb893f95ba3a8013f09de1e5569def2"},
-]
-bleach = [
-    {file = "bleach-4.1.0-py2.py3-none-any.whl", hash = "sha256:4d2651ab93271d1129ac9cbc679f524565cc8a1b791909c4a51eac4446a15994"},
-    {file = "bleach-4.1.0.tar.gz", hash = "sha256:0900d8b37eba61a802ee40ac0061f8c2b5dee29c1927dd1d233e075ebf5a71da"},
-]
-"boolean.py" = [
-    {file = "boolean.py-3.8-py2.py3-none-any.whl", hash = "sha256:d75da0fd0354425fa64f6bbc6cec6ae1485d0eec3447b73187ff8cbf9b572e26"},
-    {file = "boolean.py-3.8.tar.gz", hash = "sha256:cc24e20f985d60cd4a3a5a1c0956dd12611159d32a75081dabd0c9ab981acaa4"},
-]
-boto3 = [
-    {file = "boto3-1.20.17-py3-none-any.whl", hash = "sha256:b832c75386a4c5b7194acea1ae82dc309fddd69e660731350235d19cf70d8014"},
-    {file = "boto3-1.20.17.tar.gz", hash = "sha256:41ea196ff71ee0255ad164790319ec158fd5048de915173e8b21226650a0512f"},
-]
-botocore = [
-    {file = "botocore-1.23.17-py3-none-any.whl", hash = "sha256:54240370476d8e67a97664d2c47df451f0e1d30e9d50ea0a88da4c2c27981159"},
-    {file = "botocore-1.23.17.tar.gz", hash = "sha256:a9753b5220b5cc1bb8078086dc8ee10aa7da482b279dd0347965e9145a557003"},
-]
-bs4 = [
-    {file = "bs4-0.0.1.tar.gz", hash = "sha256:36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a"},
-]
-calendarweek = [
-    {file = "calendarweek-0.5.0-py3-none-any.whl", hash = "sha256:f2003e6e0264d3d1320fc99ae6d70e60174c2664e5640c6aa31ad38e229d942d"},
-    {file = "calendarweek-0.5.0.tar.gz", hash = "sha256:32f5c8663799a2f5a0b8909976c7a3ae77397acd7e7c31d1456ece5b452988a5"},
-]
-celery = [
-    {file = "celery-5.1.2-py3-none-any.whl", hash = "sha256:9dab2170b4038f7bf10ef2861dbf486ddf1d20592290a1040f7b7a1259705d42"},
-    {file = "celery-5.1.2.tar.gz", hash = "sha256:8d9a3de9162965e97f8e8cc584c67aad83b3f7a267584fa47701ed11c3e0d4b0"},
-]
-celery-haystack-ng = [
-    {file = "celery-haystack-ng-0.20.post2.tar.gz", hash = "sha256:d2e077851f13dddc36fc86134c7c8a937e46ae75e576eb8e77e03b03977fc7bb"},
-    {file = "celery_haystack_ng-0.20.post2-py2.py3-none-any.whl", hash = "sha256:a13e00f2c29411b06c6cdf59ad6a90b6c158e3384e7ec6d6d64f6a69e8ff299a"},
-]
-celery-progress = [
-    {file = "celery-progress-0.1.1.tar.gz", hash = "sha256:b2622d1b410a763412810f0293153c984f4a0220b76769bd701b5b45e583ddad"},
-    {file = "celery_progress-0.1.1-py3-none-any.whl", hash = "sha256:36a1e58b4408c9bf6aa63908204b50960b005db8e13f3c94ce6f8d6a2a4d4a6c"},
-]
-certifi = [
-    {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
-    {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
-]
-cffi = [
-    {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"},
-    {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"},
-    {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"},
-    {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"},
-    {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"},
-    {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"},
-    {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"},
-    {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"},
-    {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"},
-    {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"},
-    {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"},
-    {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"},
-    {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"},
-    {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"},
-    {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"},
-    {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"},
-    {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"},
-    {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"},
-    {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"},
-    {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"},
-    {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"},
-    {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"},
-    {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"},
-    {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"},
-    {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"},
-    {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"},
-    {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"},
-    {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"},
-    {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"},
-    {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"},
-    {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"},
-    {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"},
-    {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"},
-    {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"},
-    {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"},
-    {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"},
-    {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"},
-    {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"},
-    {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"},
-    {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"},
-    {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"},
-    {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"},
-    {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"},
-    {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"},
-    {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"},
-    {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"},
-    {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"},
-    {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"},
-    {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"},
-    {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"},
-]
-charset-normalizer = [
-    {file = "charset-normalizer-2.0.8.tar.gz", hash = "sha256:735e240d9a8506778cd7a453d97e817e536bb1fc29f4f6961ce297b9c7a917b0"},
-    {file = "charset_normalizer-2.0.8-py3-none-any.whl", hash = "sha256:83fcdeb225499d6344c8f7f34684c2981270beacc32ede2e669e94f7fa544405"},
-]
-click = [
-    {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
-    {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
-]
-click-didyoumean = [
-    {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"},
-    {file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"},
-]
-click-plugins = [
-    {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"},
-]
-click-repl = [
-    {file = "click-repl-0.2.0.tar.gz", hash = "sha256:cd12f68d745bf6151210790540b4cb064c7b13e571bc64b6957d98d120dacfd8"},
-    {file = "click_repl-0.2.0-py3-none-any.whl", hash = "sha256:94b3fbbc9406a236f176e0506524b2937e4b23b6f4c0c0b2a0a83f8a64e9194b"},
-]
-colorama = [
-    {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
-    {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
-]
-colour = [
-    {file = "colour-0.1.5-py2.py3-none-any.whl", hash = "sha256:33f6db9d564fadc16e59921a56999b79571160ce09916303d35346dddc17978c"},
-    {file = "colour-0.1.5.tar.gz", hash = "sha256:af20120fefd2afede8b001fbef2ea9da70ad7d49fafdb6489025dae8745c3aee"},
-]
-configobj = [
-    {file = "configobj-5.0.6.tar.gz", hash = "sha256:a2f5650770e1c87fb335af19a9b7eb73fc05ccf22144eb68db7d00cd2bcb0902"},
-]
-coverage = [
-    {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"},
-    {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"},
-    {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"},
-    {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"},
-    {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"},
-    {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"},
-    {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"},
-    {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"},
-    {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"},
-    {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"},
-    {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"},
-    {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"},
-    {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"},
-    {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"},
-    {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"},
-    {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"},
-    {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"},
-    {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"},
-    {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"},
-    {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"},
-    {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"},
-    {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"},
-    {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"},
-    {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"},
-    {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"},
-    {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"},
-    {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"},
-    {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"},
-    {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"},
-    {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"},
-    {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"},
-    {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"},
-    {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"},
-    {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"},
-    {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"},
-    {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"},
-    {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"},
-    {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"},
-    {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"},
-    {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"},
-    {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"},
-    {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"},
-    {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"},
-    {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"},
-    {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"},
-    {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"},
-    {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"},
-]
-cryptography = [
-    {file = "cryptography-36.0.0-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:9511416e85e449fe1de73f7f99b21b3aa04fba4c4d335d30c486ba3756e3a2a6"},
-    {file = "cryptography-36.0.0-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:97199a13b772e74cdcdb03760c32109c808aff7cd49c29e9cf4b7754bb725d1d"},
-    {file = "cryptography-36.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:494106e9cd945c2cadfce5374fa44c94cfadf01d4566a3b13bb487d2e6c7959e"},
-    {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6fbbbb8aab4053fa018984bb0e95a16faeb051dd8cca15add2a27e267ba02b58"},
-    {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:684993ff6f67000a56454b41bdc7e015429732d65a52d06385b6e9de6181c71e"},
-    {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c702855cd3174666ef0d2d13dcc879090aa9c6c38f5578896407a7028f75b9f"},
-    {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d91bc9f535599bed58f6d2e21a2724cb0c3895bf41c6403fe881391d29096f1d"},
-    {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:b17d83b3d1610e571fedac21b2eb36b816654d6f7496004d6a0d32f99d1d8120"},
-    {file = "cryptography-36.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:8982c19bb90a4fa2aad3d635c6d71814e38b643649b4000a8419f8691f20ac44"},
-    {file = "cryptography-36.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:24469d9d33217ffd0ce4582dfcf2a76671af115663a95328f63c99ec7ece61a4"},
-    {file = "cryptography-36.0.0-cp36-abi3-win32.whl", hash = "sha256:f6a5a85beb33e57998dc605b9dbe7deaa806385fdf5c4810fb849fcd04640c81"},
-    {file = "cryptography-36.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:2deab5ec05d83ddcf9b0916319674d3dae88b0e7ee18f8962642d3cde0496568"},
-    {file = "cryptography-36.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2049f8b87f449fc6190350de443ee0c1dd631f2ce4fa99efad2984de81031681"},
-    {file = "cryptography-36.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a776bae1629c8d7198396fd93ec0265f8dd2341c553dc32b976168aaf0e6a636"},
-    {file = "cryptography-36.0.0-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:aa94d617a4cd4cdf4af9b5af65100c036bce22280ebb15d8b5262e8273ebc6ba"},
-    {file = "cryptography-36.0.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:5c49c9e8fb26a567a2b3fa0343c89f5d325447956cc2fc7231c943b29a973712"},
-    {file = "cryptography-36.0.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ef216d13ac8d24d9cd851776662f75f8d29c9f2d05cdcc2d34a18d32463a9b0b"},
-    {file = "cryptography-36.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:231c4a69b11f6af79c1495a0e5a85909686ea8db946935224b7825cfb53827ed"},
-    {file = "cryptography-36.0.0-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:f92556f94e476c1b616e6daec5f7ddded2c082efa7cee7f31c7aeda615906ed8"},
-    {file = "cryptography-36.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d73e3a96c38173e0aa5646c31bf8473bc3564837977dd480f5cbeacf1d7ef3a3"},
-    {file = "cryptography-36.0.0.tar.gz", hash = "sha256:52f769ecb4ef39865719aedc67b4b7eae167bafa48dbc2a26dd36fa56460507f"},
-]
-cssselect2 = [
-    {file = "cssselect2-0.4.1-py3-none-any.whl", hash = "sha256:2f4a9f20965367bae459e3bb42561f7927e0cfe5b7ea1692757cf67ef5d7dace"},
-    {file = "cssselect2-0.4.1.tar.gz", hash = "sha256:93fbb9af860e95dd40bf18c3b2b6ed99189a07c0f29ba76f9c5be71344664ec8"},
-]
-curlylint = [
-    {file = "curlylint-0.13.0-py3-none-any.whl", hash = "sha256:63e5fc98f99c7b0eab0c4e3390356ad569b7bb3eecb6da115e6cb9ee98eb738f"},
-    {file = "curlylint-0.13.0.tar.gz", hash = "sha256:0d38839f126b3fdd2561a7efa65d15a1c842aac98e555b2381f7c79478e1ff25"},
-]
-decorator = [
-    {file = "decorator-5.1.0-py3-none-any.whl", hash = "sha256:7b12e7c3c6ab203a29e157335e9122cb03de9ab7264b137594103fd4a683b374"},
-    {file = "decorator-5.1.0.tar.gz", hash = "sha256:e59913af105b9860aa2c8d3272d9de5a56a4e608db9a2f167a8480b323d529a7"},
-]
-defusedxml = [
-    {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"},
-    {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"},
-]
-deprecated = [
-    {file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"},
-    {file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"},
-]
-dj-database-url = [
-    {file = "dj-database-url-0.5.0.tar.gz", hash = "sha256:4aeaeb1f573c74835b0686a2b46b85990571159ffc21aa57ecd4d1e1cb334163"},
-    {file = "dj_database_url-0.5.0-py2.py3-none-any.whl", hash = "sha256:851785365761ebe4994a921b433062309eb882fedd318e1b0fcecc607ed02da9"},
-]
-django = [
-    {file = "Django-3.2.9-py3-none-any.whl", hash = "sha256:e22c9266da3eec7827737cde57694d7db801fedac938d252bf27377cec06ed1b"},
-    {file = "Django-3.2.9.tar.gz", hash = "sha256:51284300f1522ffcdb07ccbdf676a307c6678659e1284f0618e5a774127a6a08"},
-]
-django-allauth = [
-    {file = "django-allauth-0.46.0.tar.gz", hash = "sha256:8217b8dc46f85812ff209fc542f4bf378f1751cdbe867008169d4c85685df50d"},
-]
-django-any-js = [
-    {file = "django-any-js-1.1.tar.gz", hash = "sha256:2972946902ba049f73bf8bb87e0a0118f77a8c9dca89438f193598bff758422f"},
-    {file = "django_any_js-1.1-py3-none-any.whl", hash = "sha256:1499934e293bbcaad29b8edaaefca87dda79eb3df1faeaaea67b80e2866ae1f8"},
-]
-django-appconf = [
-    {file = "django-appconf-1.0.5.tar.gz", hash = "sha256:be3db0be6c81fa84742000b89a81c016d70ae66a7ccb620cdef592b1f1a6aaa4"},
-    {file = "django_appconf-1.0.5-py3-none-any.whl", hash = "sha256:ae9f864ee1958c815a965ed63b3fba4874eec13de10236ba063a788f9a17389d"},
-]
-django-auth-ldap = [
-    {file = "django-auth-ldap-3.0.0.tar.gz", hash = "sha256:1f2d5c562d9ba9a5e9a64099ae9798e1a63840a11afe4d1c4a9c74121f066eaa"},
-    {file = "django_auth_ldap-3.0.0-py3-none-any.whl", hash = "sha256:19ee19034f344d9efd07ed88d3187e256ec33ae39d6a47222083b89f7d35c5f6"},
-]
-django-bleach = [
-    {file = "django-bleach-1.0.0.tar.gz", hash = "sha256:2586b90d641d4d7e70ee353570ad33d3625ed4b97036a3ea5b03ea1bb5bbeccd"},
-    {file = "django_bleach-1.0.0-py2.py3-none-any.whl", hash = "sha256:60074a4f4bc8d5200fdb2e03dce16fb4913427698b64570bc3e1a7ea1b8c3cf7"},
-]
-django-cachalot = [
-    {file = "django-cachalot-2.4.4.tar.gz", hash = "sha256:8489a74e9a689437f81fa61705b154a4be3cac1b62b61f68215d79668c9ac037"},
-    {file = "django_cachalot-2.4.4-py3-none-any.whl", hash = "sha256:f8995a85197ac290e63a2f97c577cc9825000fe081b08d5c7d35079e7f1daa43"},
-]
-django-cache-memoize = [
-    {file = "django-cache-memoize-0.1.10.tar.gz", hash = "sha256:63e8faa245a41c0dbad843807e9f21a6e59eba8e6e50df310fdf6485a6749843"},
-    {file = "django_cache_memoize-0.1.10-py3-none-any.whl", hash = "sha256:676299313079cde9242ae84db0160e80b1d44e8dd6bc9b1f4f1247e11b30c9e0"},
-]
-django-celery-beat = [
-    {file = "django-celery-beat-2.2.1.tar.gz", hash = "sha256:97ae5eb309541551bdb07bf60cc57cadacf42a74287560ced2d2c06298620234"},
-    {file = "django_celery_beat-2.2.1-py2.py3-none-any.whl", hash = "sha256:ab43049634fd18dc037927d7c2c7d5f67f95283a20ebbda55f42f8606412e66c"},
-]
-django-celery-email = [
-    {file = "django-celery-email-3.0.0.tar.gz", hash = "sha256:5546cbba80952cc3b8a0ffa4206ce90a4a996a7ffd1c385a2bdb65903ca18ece"},
-    {file = "django_celery_email-3.0.0-py2.py3-none-any.whl", hash = "sha256:0f72da39cb2ea83c69440566e87f27cd72f68f247f98ce99fb29889fcf329406"},
-]
-django-celery-results = [
-    {file = "django_celery_results-2.2.0-py2.py3-none-any.whl", hash = "sha256:d5f83fad9091e52cd6dbb3ca80632153ad14b6cdac4d73258e040f92717237cb"},
-    {file = "django_celery_results-2.2.0.tar.gz", hash = "sha256:cc0285090a306f97f1d4b7929ed98af0475bf6db2568976b3387de4fbe812edc"},
-]
-django-ckeditor = [
-    {file = "django-ckeditor-6.2.0.tar.gz", hash = "sha256:df64dc9e62790ef824f609605d31be847bdbce1cc7aa94e49bd5ca60d7aa79bb"},
-    {file = "django_ckeditor-6.2.0-py2.py3-none-any.whl", hash = "sha256:9f66420907e41f5b4e698fa5671a00a86995776735f2c4696174aed4640fcbd8"},
-]
-django-cleanup = [
-    {file = "django-cleanup-5.2.0.tar.gz", hash = "sha256:909d10ff574f5ce1a40fa63bd5c94c9ed866fd7ae770994c46cdf66c3db3e846"},
-    {file = "django_cleanup-5.2.0-py2.py3-none-any.whl", hash = "sha256:193cf69de54b9fc0a0f4547edbb3a63bbe01728cb029f9f4b7912098cc1bced7"},
-]
-django-colorfield = [
-    {file = "django-colorfield-0.5.0.tar.gz", hash = "sha256:670ef72ded093360c9781fedd33d9ead267b34481139d4ab240dfacea5cf0abf"},
-    {file = "django_colorfield-0.5.0-py3-none-any.whl", hash = "sha256:aba49684ba1fce80af15237b49ca4a504e49e525c2897db2cce5705b2fa51f3b"},
-]
-django-dbbackup = [
-    {file = "django-dbbackup-3.3.0.tar.gz", hash = "sha256:bb109735cae98b64ad084e5b461b7aca2d7b39992f10c9ed9435e3ebb6fb76c8"},
-]
-django-debug-toolbar = [
-    {file = "django-debug-toolbar-3.2.2.tar.gz", hash = "sha256:8c5b13795d4040008ee69ba82dcdd259c49db346cf7d0de6e561a49d191f0860"},
-    {file = "django_debug_toolbar-3.2.2-py3-none-any.whl", hash = "sha256:d7bab7573fab35b0fd029163371b7182f5826c13da69734beb675c761d06a4d3"},
-]
-django-dynamic-preferences = [
-    {file = "django-dynamic-preferences-1.11.0.tar.gz", hash = "sha256:f214c938b5872a17647e2b2ccfd9ad00a90a3c6c4aa83fa65d3c5c446e7a66c7"},
-    {file = "django_dynamic_preferences-1.11.0-py2.py3-none-any.whl", hash = "sha256:31aecebcbfcfb970b78cfa3e5f8cc9f77638efe8e7c90f205a48b01c45ee5002"},
-]
-django-extensions = [
-    {file = "django-extensions-3.1.5.tar.gz", hash = "sha256:28e1e1bf49f0e00307ba574d645b0af3564c981a6dfc87209d48cb98f77d0b1a"},
-    {file = "django_extensions-3.1.5-py3-none-any.whl", hash = "sha256:9238b9e016bb0009d621e05cf56ea8ce5cce9b32e91ad2026996a7377ca28069"},
-]
-django-favicon-plus-reloaded = [
-    {file = "django-favicon-plus-reloaded-1.1.3.tar.gz", hash = "sha256:36c2a1cefc201df8bd132492e2440ccdc3d9ceb8e421b2ca181a4704ebacd190"},
-    {file = "django_favicon_plus_reloaded-1.1.3-py3-none-any.whl", hash = "sha256:a60b438360e82bf7075b856ff6a80bae20c825373a58deac627810e478c42be3"},
-]
-django-filter = [
-    {file = "django-filter-2.4.0.tar.gz", hash = "sha256:84e9d5bb93f237e451db814ed422a3a625751cbc9968b484ecc74964a8696b06"},
-    {file = "django_filter-2.4.0-py3-none-any.whl", hash = "sha256:e00d32cebdb3d54273c48f4f878f898dced8d5dfaad009438fe61ebdf535ace1"},
-]
-django-formtools = [
-    {file = "django-formtools-2.3.tar.gz", hash = "sha256:9663b6eca64777b68d6d4142efad8597fe9a685924673b25aa8a1dcff4db00c3"},
-    {file = "django_formtools-2.3-py3-none-any.whl", hash = "sha256:4699937e19ee041d803943714fe0c1c7ad4cab802600eb64bbf4cdd0a1bfe7d9"},
-]
-django-guardian = [
-    {file = "django-guardian-2.4.0.tar.gz", hash = "sha256:c58a68ae76922d33e6bdc0e69af1892097838de56e93e78a8361090bcd9f89a0"},
-    {file = "django_guardian-2.4.0-py3-none-any.whl", hash = "sha256:440ca61358427e575323648b25f8384739e54c38b3d655c81d75e0cd0d61b697"},
-]
-django-haystack = [
-    {file = "django-haystack-3.1.1.tar.gz", hash = "sha256:6d05756b95d7d5ec1dbd4668eb999ced1504b47f588e2e54be53b1404c516a82"},
-    {file = "django_haystack-3.1.1-py3-none-any.whl", hash = "sha256:970ebc6362f3f84861209154ec34b57f3e87d2d7922f948df80177f0e2e3ced7"},
-]
-django-health-check = [
-    {file = "django-health-check-3.16.4.tar.gz", hash = "sha256:334bcbbb9273a6dbd9c928e78474306e623dfb38cc442281cb9fd230a20a7fdb"},
-    {file = "django_health_check-3.16.4-py2.py3-none-any.whl", hash = "sha256:86a8869d67e72394a1dd73e37819a7d2cfd915588b96927fda611d7451fd4735"},
-]
-django-image-cropping = [
-    {file = "django-image-cropping-1.6.1.tar.gz", hash = "sha256:bc5e34c3cbc28a79034a8ce20921e83b399ede087c99e3a1c699652b4214dc01"},
-    {file = "django_image_cropping-1.6.1-py3-none-any.whl", hash = "sha256:da5c1c8cf0d83017a6e53d24eefd0e98cfcb84a61adb0154295827c88a37eb2a"},
-]
-django-impersonate = [
-    {file = "django-impersonate-1.7.3.tar.gz", hash = "sha256:282003957577c7143fe31e5861f8fffdf6fe0c25557aedb28fcf8b11474eaa23"},
-]
-django-ipware = [
-    {file = "django-ipware-4.0.0.tar.gz", hash = "sha256:1294f916f3b3475e40e1b0ec1bd320aa2397978eae672721c81cbc2ed517e9ee"},
-    {file = "django_ipware-4.0.0-py2.py3-none-any.whl", hash = "sha256:116bd0d7940f09bf7ffd465943992e23d87e772a9d6c0d3a57b74040589a383b"},
-]
-django-js-asset = [
-    {file = "django-js-asset-1.2.2.tar.gz", hash = "sha256:c163ae80d2e0b22d8fb598047cd0dcef31f81830e127cfecae278ad574167260"},
-    {file = "django_js_asset-1.2.2-py2.py3-none-any.whl", hash = "sha256:8ec12017f26eec524cab436c64ae73033368a372970af4cf42d9354fcb166bdd"},
-]
-django-js-reverse = [
-    {file = "django-js-reverse-0.9.1.tar.gz", hash = "sha256:2a392d169f44e30b883c30dfcfd917a14167ce8fe196c99d2385b31c90d77aa0"},
-    {file = "django_js_reverse-0.9.1-py2.py3-none-any.whl", hash = "sha256:8134c2ab6307c945edfa90671ca65e85d6c1754d48566bdd6464be259cc80c30"},
-]
-django-jsonstore = [
-    {file = "django-jsonstore-0.5.0.tar.gz", hash = "sha256:896dc10b08f59807eda1c6cebf43cd26e50d0db29d13495c027dc31e464be3c3"},
-    {file = "django_jsonstore-0.5.0-py2-none-any.whl", hash = "sha256:9630c1fb43ae9f8e32733c5cf7d4c3775ba6f08532f517c64025053352d72844"},
-]
-django-maintenance-mode = [
-    {file = "django-maintenance-mode-0.16.1.tar.gz", hash = "sha256:da1f77f431ab5c55913459adb3c259e091f783ffc72de701690826aaaccce4ca"},
-    {file = "django_maintenance_mode-0.16.1-py3-none-any.whl", hash = "sha256:1bfac0b34429a9f6dbb0db169fb753f49f24ca155ae148d7526ff395303c158d"},
-]
-django-material = [
-    {file = "django-material-1.10.0.tar.gz", hash = "sha256:3cc015500939a63606c1522119fddc4c86facb6d1bbfa8fc76bdc8878e8252f5"},
-    {file = "django_material-1.10.0-py2.py3-none-any.whl", hash = "sha256:50e4764fa0cb504cf1f6d497f86c6294ff3fbf43bdbf0e4c612e3681fa9000a0"},
-]
-django-menu-generator-ng = [
-    {file = "django-menu-generator-ng-1.2.3.tar.gz", hash = "sha256:0c21a094b094add909655728b6b2d4a8baa5a2047da8f649be52589bea0e3ba2"},
-]
-django-model-utils = [
-    {file = "django-model-utils-4.2.0.tar.gz", hash = "sha256:e7a95e102f9c9653427eadab980d5d59e1dea972913b9c9e01ac37f86bba0ddf"},
-    {file = "django_model_utils-4.2.0-py3-none-any.whl", hash = "sha256:a768a25c80514e0ad4e4a6f9c02c44498985f36c5dfdea47b5b1e8cf994beba6"},
-]
-django-oauth-toolkit = [
-    {file = "django-oauth-toolkit-1.5.0.tar.gz", hash = "sha256:650e5ef2244d1d8db8f507137e0d1e8b8aad1f4086a4a610526e8851f9a38308"},
-    {file = "django_oauth_toolkit-1.5.0-py3-none-any.whl", hash = "sha256:b2e346a7c1e222774bfb370f21b556b92b408395b4c23914e2d1b241b2e5376a"},
-]
-django-otp = [
-    {file = "django-otp-1.1.3.tar.gz", hash = "sha256:f002c71d4ea7f514590be00492980d3c87397b73dc20542e1c4fc00b66f2dda1"},
-    {file = "django_otp-1.1.3-py3-none-any.whl", hash = "sha256:8637be826c0465d0fd1710e4472efe9fc83883853a2141fefdbace9358d20003"},
-]
-django-otp-yubikey = [
-    {file = "django-otp-yubikey-1.0.1.tar.gz", hash = "sha256:5917b9134fa408d12b94bdb4d3cac23e4586ae99c3a42fcb1d2c287c182e6c77"},
-    {file = "django_otp_yubikey-1.0.1-py2.py3-none-any.whl", hash = "sha256:5a1b59be47088a3eccf376ca27d708bdcccfeb30324bb5ca01ed2d669b73756c"},
-]
-django-phonenumber-field = [
-    {file = "django-phonenumber-field-5.2.0.tar.gz", hash = "sha256:52b2e5970133ec5ab701218b802f7ab237229854dc95fd239b7e9e77dc43731d"},
-    {file = "django_phonenumber_field-5.2.0-py3-none-any.whl", hash = "sha256:5547fb2b2cc690a306ba77a5038419afc8fa8298a486fb7895008e9067cc7e75"},
-]
-django-polymorphic = [
-    {file = "django-polymorphic-3.1.0.tar.gz", hash = "sha256:d6955b5308bf6e41dcb22ba7c96f00b51dfa497a8a5ab1e9c06c7951bf417bf8"},
-    {file = "django_polymorphic-3.1.0-py3-none-any.whl", hash = "sha256:08bc4f4f4a773a19b2deced5a56deddd1ef56ebd15207bf4052e2901c25ef57e"},
-]
-django-prometheus = [
-    {file = "django-prometheus-2.1.0.tar.gz", hash = "sha256:dd3f8da1399140fbef5c00d1526a23d1ade286b144281c325f8e409a781643f2"},
-    {file = "django_prometheus-2.1.0-py2.py3-none-any.whl", hash = "sha256:c338d6efde1ca336e90c540b5e87afe9287d7bcc82d651a778f302b0be17a933"},
-]
-django-redis = [
-    {file = "django-redis-5.1.0.tar.gz", hash = "sha256:98fb3d31633a1addea1aeb558a647359908bbcf78c0833f99496c5348fe3c1b4"},
-    {file = "django_redis-5.1.0-py3-none-any.whl", hash = "sha256:bf75bce0d6f65c3a6165dd6789506c8d22238f3bfaf7c4ad447e55afbc5b68cb"},
-]
-django-render-block = [
-    {file = "django-render-block-0.8.1.tar.gz", hash = "sha256:edbc5d444cc50f3eb3387cf17f6f1014bf19d6018f680861cdeae9e0306003fa"},
-    {file = "django_render_block-0.8.1-py3-none-any.whl", hash = "sha256:903969efd0949f750c5fe71affe6e6b1ea66d03005c102a67fda36d5b9f4e1e1"},
-]
-django-reversion = [
-    {file = "django-reversion-4.0.1.tar.gz", hash = "sha256:6991f16e5d3a972912db3d56e3a714d10b07becd566ab87f85f2e9b671981339"},
-    {file = "django_reversion-4.0.1-py3-none-any.whl", hash = "sha256:2e40ed41e08cdd83a05dc70a1974feface52a61ba7d289727117163052081ae6"},
-]
-django-sass-processor = [
-    {file = "django-sass-processor-1.0.0.tar.gz", hash = "sha256:cb90efee38cd7b0fe727c78d8993ad7804de33f40328200dfc1a481307ef0466"},
-]
-django-select2 = [
-    {file = "django-select2-7.9.0.tar.gz", hash = "sha256:4494cf5fa13333ddbd34e4dbeca4b44a22ed6367342cccd24f6f122f6ef4a20b"},
-    {file = "django_select2-7.9.0-py2.py3-none-any.whl", hash = "sha256:40d42643b459f79c31598fa5645f5f7e8edda8350a78761ab6c06105ca588872"},
-]
-django-storages = [
-    {file = "django-storages-1.12.3.tar.gz", hash = "sha256:a475edb2f0f04c4f7e548919a751ecd50117270833956ed5bd585c0575d2a5e7"},
-    {file = "django_storages-1.12.3-py3-none-any.whl", hash = "sha256:204a99f218b747c46edbfeeb1310d357f83f90fa6a6024d8d0a3f422570cee84"},
-]
-django-stubs = [
-    {file = "django-stubs-1.9.0.tar.gz", hash = "sha256:664843091636a917faf5256d028476559dc360fdef9050b6df87ab61b21607bf"},
-    {file = "django_stubs-1.9.0-py3-none-any.whl", hash = "sha256:59c9f81af64d214b1954eaf90f037778c8d2b9c2de946a3cda177fefcf588fbd"},
-]
-django-stubs-ext = [
-    {file = "django-stubs-ext-0.3.1.tar.gz", hash = "sha256:783c198d7e39a41be0b90fd843fa2770243a642922af679be4b19e03b82c8c28"},
-    {file = "django_stubs_ext-0.3.1-py3-none-any.whl", hash = "sha256:a51a3e9e844d4e1cacaaedbb33bf3def78a3956eed5d9575a640bd97ccd99cec"},
-]
-django-tables2 = [
-    {file = "django-tables2-2.4.1.tar.gz", hash = "sha256:6c72dd208358539e789e4c0efd7d151e43283a4aa4093a35f44c43489e7ddeaa"},
-    {file = "django_tables2-2.4.1-py2.py3-none-any.whl", hash = "sha256:50762bf3d7c61a4eb70e763c3e278650d7266bb78d0497fc8fafcf4e507c9a64"},
-]
-django-templated-email = [
-    {file = "django-templated-email-3.0.0.tar.gz", hash = "sha256:49d61840ec551e640adaf341146e94d6f9058ae01df964480850bf988046e5eb"},
-    {file = "django_templated_email-3.0.0-py3-none-any.whl", hash = "sha256:bf1b68ffe6c8794c0c50e2ce20e3a166c6d511b3879abbd3cf059a3fc2fe2e60"},
-]
-django-timezone-field = [
-    {file = "django-timezone-field-4.2.1.tar.gz", hash = "sha256:97780cde658daa5094ae515bb55ca97c1352928ab554041207ad515dee3fe971"},
-    {file = "django_timezone_field-4.2.1-py3-none-any.whl", hash = "sha256:6dc782e31036a58da35b553bd00c70f112d794700025270d8a6a4c1d2e5b26c6"},
-]
-django-titofisto = [
-    {file = "django-titofisto-0.2.0.tar.gz", hash = "sha256:e181081a8c9c5c8da9f15458d996624f9e19c98d2882a5d4f199b39dd0787ad6"},
-    {file = "django_titofisto-0.2.0-py3-none-any.whl", hash = "sha256:38d5298fe64d3c12a3a48767d8f1af4bd86cfc12787af0faaf3e141a29a12bb8"},
-]
-django-two-factor-auth = [
-    {file = "django-two-factor-auth-1.13.1.tar.gz", hash = "sha256:a20e03d256fd9fd668988545f052cedcc47e5a981888562e5e27d0bb83deae89"},
-    {file = "django_two_factor_auth-1.13.1-py2.py3-none-any.whl", hash = "sha256:d270d4288731233621a9462a89a8dfed2dcb86fa354125c816a89772d55f9e29"},
-]
-django-uwsgi-ng = [
-    {file = "django-uwsgi-ng-1.1.2.tar.gz", hash = "sha256:1df2ffa642f7a831bd8d7f7e459f7b0821113b37174ccf4bf977e4467d45d9b3"},
-    {file = "django_uwsgi_ng-1.1.2-py3-none-any.whl", hash = "sha256:8b1a489a1ed9e56da0efadfa86ec306b532e5cd953fe34b234aaefc26898c649"},
-]
-django-widget-tweaks = [
-    {file = "django-widget-tweaks-1.4.9.tar.gz", hash = "sha256:19bcb66a4a9e68493ced04e7124882d753c5be517ed001556f9e35a40147f545"},
-    {file = "django_widget_tweaks-1.4.9-py2.py3-none-any.whl", hash = "sha256:d6c64fbf92cd2df9031f597c1374982233c05a1190d295c39d1c57ce007569c7"},
-]
-django-yarnpkg = [
-    {file = "django-yarnpkg-6.0.1.tar.gz", hash = "sha256:aa059347b246c6f242401581d2c129bdcb45aa726be59fe2f288762a9843348a"},
-]
-djangorestframework = [
-    {file = "djangorestframework-3.12.4-py3-none-any.whl", hash = "sha256:6d1d59f623a5ad0509fe0d6bfe93cbdfe17b8116ebc8eda86d45f6e16e819aaf"},
-    {file = "djangorestframework-3.12.4.tar.gz", hash = "sha256:f747949a8ddac876e879190df194b925c177cdeb725a099db1460872f7c0a7f2"},
-]
-docutils = [
-    {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"},
-    {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"},
-]
-dparse = [
-    {file = "dparse-0.5.1-py3-none-any.whl", hash = "sha256:e953a25e44ebb60a5c6efc2add4420c177f1d8404509da88da9729202f306994"},
-    {file = "dparse-0.5.1.tar.gz", hash = "sha256:a1b5f169102e1c894f9a7d5ccf6f9402a836a5d24be80a986c7ce9eaed78f367"},
-]
-dynaconf = [
-    {file = "dynaconf-3.1.7-py2.py3-none-any.whl", hash = "sha256:f52fe5db7622da56a552275e8f64e4df46e3b4ae11158831b042e8ba2f6d1c96"},
-    {file = "dynaconf-3.1.7.tar.gz", hash = "sha256:e9d80b46ba4d9372f2f40c812594c963f74178140c0b596e57f2881001fc4d35"},
-]
-easy-thumbnails = [
-    {file = "easy-thumbnails-2.8.tar.gz", hash = "sha256:fd2249d936671847fc54a2d6c8c87bcca8f803001967dd03bab6b8bcb7590825"},
-    {file = "easy_thumbnails-2.8-py3-none-any.whl", hash = "sha256:f2a9b82b9ddd1b4342ef92009589d76e1dc61082970302b9bbb7510f054b4d77"},
-]
-flake8 = [
-    {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"},
-    {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"},
-]
-flake8-bandit = [
-    {file = "flake8_bandit-2.1.2.tar.gz", hash = "sha256:687fc8da2e4a239b206af2e54a90093572a60d0954f3054e23690739b0b0de3b"},
-]
-flake8-black = [
-    {file = "flake8-black-0.2.3.tar.gz", hash = "sha256:c199844bc1b559d91195ebe8620216f21ed67f2cc1ff6884294c91a0d2492684"},
-    {file = "flake8_black-0.2.3-py3-none-any.whl", hash = "sha256:cc080ba5b3773b69ba102b6617a00cc4ecbad8914109690cfda4d565ea435d96"},
-]
-flake8-builtins = [
-    {file = "flake8-builtins-1.5.3.tar.gz", hash = "sha256:09998853b2405e98e61d2ff3027c47033adbdc17f9fe44ca58443d876eb00f3b"},
-    {file = "flake8_builtins-1.5.3-py2.py3-none-any.whl", hash = "sha256:7706babee43879320376861897e5d1468e396a40b8918ed7bccf70e5f90b8687"},
-]
-flake8-django = [
-    {file = "flake8-django-1.1.1.tar.gz", hash = "sha256:fb4e8f669d3cf44297bb6e1c5d0a358ab0aba373cd4c69268cf2798de6bcbd9b"},
-    {file = "flake8_django-1.1.1-py3-none-any.whl", hash = "sha256:c71da0e61b6119dae91cbffdbdb00f1d6ebe3f5d0c43f5bf136929997ab0b72d"},
-]
-flake8-docstrings = [
-    {file = "flake8-docstrings-1.6.0.tar.gz", hash = "sha256:9fe7c6a306064af8e62a055c2f61e9eb1da55f84bb39caef2b84ce53708ac34b"},
-    {file = "flake8_docstrings-1.6.0-py2.py3-none-any.whl", hash = "sha256:99cac583d6c7e32dd28bbfbef120a7c0d1b6dde4adb5a9fd441c4227a6534bde"},
-]
-flake8-fixme = [
-    {file = "flake8-fixme-1.1.1.tar.gz", hash = "sha256:50cade07d27a4c30d4f12351478df87339e67640c83041b664724bda6d16f33a"},
-    {file = "flake8_fixme-1.1.1-py2.py3-none-any.whl", hash = "sha256:226a6f2ef916730899f29ac140bed5d4a17e5aba79f00a0e3ae1eff1997cb1ac"},
-]
-flake8-isort = [
-    {file = "flake8-isort-4.1.1.tar.gz", hash = "sha256:d814304ab70e6e58859bc5c3e221e2e6e71c958e7005239202fee19c24f82717"},
-    {file = "flake8_isort-4.1.1-py3-none-any.whl", hash = "sha256:c4e8b6dcb7be9b71a02e6e5d4196cefcef0f3447be51e82730fb336fff164949"},
-]
-flake8-mypy = [
-    {file = "flake8-mypy-17.8.0.tar.gz", hash = "sha256:47120db63aff631ee1f84bac6fe8e64731dc66da3efc1c51f85e15ade4a3ba18"},
-    {file = "flake8_mypy-17.8.0-py35.py36-none-any.whl", hash = "sha256:cff009f4250e8391bf48990093cff85802778c345c8449d6498b62efefeebcbc"},
-]
-flake8-polyfill = [
-    {file = "flake8-polyfill-1.0.2.tar.gz", hash = "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"},
-    {file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"},
-]
-flake8-rst-docstrings = [
-    {file = "flake8-rst-docstrings-0.2.3.tar.gz", hash = "sha256:3045794e1c8467fba33aaea5c246b8369efc9c44ef8b0b20199bb6df7a4bd47b"},
-    {file = "flake8_rst_docstrings-0.2.3-py3-none-any.whl", hash = "sha256:565bbb391d7e4d0042924102221e9857ad72929cdd305b26501736ec22c1451a"},
-]
-freezegun = [
-    {file = "freezegun-1.1.0-py2.py3-none-any.whl", hash = "sha256:2ae695f7eb96c62529f03a038461afe3c692db3465e215355e1bb4b0ab408712"},
-    {file = "freezegun-1.1.0.tar.gz", hash = "sha256:177f9dd59861d871e27a484c3332f35a6e3f5d14626f2bf91be37891f18927f3"},
-]
-gitdb = [
-    {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"},
-    {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"},
-]
-gitpython = [
-    {file = "GitPython-3.1.24-py3-none-any.whl", hash = "sha256:dc0a7f2f697657acc8d7f89033e8b1ea94dd90356b2983bca89dc8d2ab3cc647"},
-    {file = "GitPython-3.1.24.tar.gz", hash = "sha256:df83fdf5e684fef7c6ee2c02fc68a5ceb7e7e759d08b694088d0cacb4eba59e5"},
-]
-h11 = [
-    {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"},
-    {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"},
-]
-haystack-redis = [
-    {file = "haystack-redis-0.0.1.tar.gz", hash = "sha256:ccfea88bdc1387c9f7f6f19e9bc062a3612039ef94cfd3e78cf59a96ddd269b2"},
-    {file = "haystack_redis-0.0.1-py3-none-any.whl", hash = "sha256:4fdeee5a9d8daadb1fed4584fd2ffbb25b1ed2315dacb97b53093756d6b54467"},
-]
-html2text = [
-    {file = "html2text-2020.1.16-py3-none-any.whl", hash = "sha256:c7c629882da0cf377d66f073329ccf34a12ed2adf0169b9285ae4e63ef54c82b"},
-    {file = "html2text-2020.1.16.tar.gz", hash = "sha256:e296318e16b059ddb97f7a8a1d6a5c1d7af4544049a01e261731d2d5cc277bbb"},
-]
-idna = [
-    {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
-    {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
-]
-imagesize = [
-    {file = "imagesize-1.3.0-py2.py3-none-any.whl", hash = "sha256:1db2f82529e53c3e929e8926a1fa9235aa82d0bd0c580359c67ec31b2fddaa8c"},
-    {file = "imagesize-1.3.0.tar.gz", hash = "sha256:cd1750d452385ca327479d45b64d9c7729ecf0b3969a58148298c77092261f9d"},
-]
-iniconfig = [
-    {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
-    {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
-]
-ipython = [
-    {file = "ipython-7.30.0-py3-none-any.whl", hash = "sha256:c8f3e07aefb9cf9e067f39686f035ce09b27a1ee602116a3030b91b6fc138ee4"},
-    {file = "ipython-7.30.0.tar.gz", hash = "sha256:d41f8e80b99690122400f9b2069b12f670246a1b4cc5d332bd6c4e2500e6d6fb"},
-]
-isort = [
-    {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
-    {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
-]
-jedi = [
-    {file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"},
-    {file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"},
-]
-jinja2 = [
-    {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"},
-    {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"},
-]
-jmespath = [
-    {file = "jmespath-0.10.0-py2.py3-none-any.whl", hash = "sha256:cdf6525904cc597730141d61b36f2e4b8ecc257c420fa2f4549bac2c2d0cb72f"},
-    {file = "jmespath-0.10.0.tar.gz", hash = "sha256:b85d0567b8666149a93172712e68920734333c0ce7e89b78b3e987f71e5ed4f9"},
-]
-jwcrypto = [
-    {file = "jwcrypto-1.0-py2.py3-none-any.whl", hash = "sha256:db93a656d9a7a35dda5a68deb5c9f301f4e60507d8aef1559e0637b9ac497137"},
-    {file = "jwcrypto-1.0.tar.gz", hash = "sha256:f88816eb0a41b8f006af978ced5f171f33782525006cdb055b536a40f4d46ac9"},
-]
-kombu = [
-    {file = "kombu-5.2.2-py3-none-any.whl", hash = "sha256:d36f0cde6a18d9eb7b6b3aa62a59bfdff7f5724689850e447eca5be8efc9d501"},
-    {file = "kombu-5.2.2.tar.gz", hash = "sha256:0f5d0763fb916808f617b886697b2be28e6bc35026f08e679697fc814b48a608"},
-]
-libsass = [
-    {file = "libsass-0.21.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:06c8776417fe930714bdc930a3d7e795ae3d72be6ac883ff72a1b8f7c49e5ffb"},
-    {file = "libsass-0.21.0-cp27-cp27m-win32.whl", hash = "sha256:a005f298f64624f313a3ac618ab03f844c71d84ae4f4a4aec4b68d2a4ffe75eb"},
-    {file = "libsass-0.21.0-cp27-cp27m-win_amd64.whl", hash = "sha256:6b984510ed94993708c0d697b4fef2d118929bbfffc3b90037be0f5ccadf55e7"},
-    {file = "libsass-0.21.0-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1e25dd9047a9392d3c59a0b869e0404f2b325a03871ee45285ee33b3664f5613"},
-    {file = "libsass-0.21.0-cp36-abi3-macosx_10_14_x86_64.whl", hash = "sha256:12f39712de38689a8b785b7db41d3ba2ea1d46f9379d81ea4595802d91fa6529"},
-    {file = "libsass-0.21.0-cp36-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e2b1a7d093f2e76dc694c17c0c285e846d0b0deb0e8b21dc852ba1a3a4e2f1d6"},
-    {file = "libsass-0.21.0-cp36-abi3-win32.whl", hash = "sha256:abc29357ee540849faf1383e1746d40d69ed5cb6d4c346df276b258f5aa8977a"},
-    {file = "libsass-0.21.0-cp36-abi3-win_amd64.whl", hash = "sha256:659ae41af8708681fa3ec73f47b9735a6725e71c3b66ff570bfce78952f2314e"},
-    {file = "libsass-0.21.0.tar.gz", hash = "sha256:d5ba529d9ce668be9380563279f3ffe988f27bc5b299c5a28453df2e0b0fbaf2"},
-]
-license-expression = [
-    {file = "license-expression-21.6.14.tar.gz", hash = "sha256:9de87a427c9a449eee7913472fb9ed03b63036295547369fdbf95f76a8b924b2"},
-    {file = "license_expression-21.6.14-py3-none-any.whl", hash = "sha256:324246eed8e138b4139fefdc0e9dc4161d5075e3929e56983966d37298dca30e"},
-]
-lxml = [
-    {file = "lxml-4.6.4-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:bbf2dc330bd44bfc0254ab37677ec60f7c7ecea55ad8ba1b8b2ea7bf20c265f5"},
-    {file = "lxml-4.6.4-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b667c51682fe9b9788c69465956baa8b6999531876ccedcafc895c74ad716cd8"},
-    {file = "lxml-4.6.4-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:72e730d33fe2e302fd07285f14624fca5e5e2fb2bb4fb2c3941e318c41c443d1"},
-    {file = "lxml-4.6.4-cp27-cp27m-win32.whl", hash = "sha256:433df8c7dde0f9e41cbf4f36b0829d50a378116ef5e962ba3881f2f5f025c7be"},
-    {file = "lxml-4.6.4-cp27-cp27m-win_amd64.whl", hash = "sha256:35752ee40f7bbf6adc9ff4e1f4b84794a3593736dcce80db32e3c2aa85e294ac"},
-    {file = "lxml-4.6.4-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ff5bb2a198ea67403bb6818705e9a4f90e0313f2215428ec51001ce56d939fb"},
-    {file = "lxml-4.6.4-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9b87727561c1150c0cc91c5d9d389448b37a7d15f0ba939ed3d1acb2f11bf6c5"},
-    {file = "lxml-4.6.4-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:45fdb2899c755138722797161547a40b3e2a06feda620cc41195ee7e97806d81"},
-    {file = "lxml-4.6.4-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:38b9de0de3aa689fe9fb9877ae1be1e83b8cf9621f7e62049d0436b9ecf4ad64"},
-    {file = "lxml-4.6.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:662523cd2a0246740225c7e32531f2e766544122e58bee70e700a024cfc0cf81"},
-    {file = "lxml-4.6.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:4aa349c5567651f34d4eaae7de6ed5b523f6d70a288f9c6fbac22d13a0784e04"},
-    {file = "lxml-4.6.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:08eb9200d88b376a8ed5e50f1dc1d1a45b49305169674002a3b5929943390591"},
-    {file = "lxml-4.6.4-cp310-cp310-win32.whl", hash = "sha256:bdc224f216ead849e902151112efef6e96c41ee1322e15d4e5f7c8a826929aee"},
-    {file = "lxml-4.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:ab6db93a2b6b66cbf62b4e4a7135f476e708e8c5c990d186584142c77d7f975a"},
-    {file = "lxml-4.6.4-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:50790313df028aa05cf22be9a8da033b86c42fa32523e4fd944827b482b17bf0"},
-    {file = "lxml-4.6.4-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6764998345552b1dfc9326a932d2bad6367c6b37a176bb73ada6b9486bf602f7"},
-    {file = "lxml-4.6.4-cp35-cp35m-win32.whl", hash = "sha256:543b239b191bb3b6d9bef5f09f1fb2be5b7eb09ab4d386aa655e4d53fbe9ff47"},
-    {file = "lxml-4.6.4-cp35-cp35m-win_amd64.whl", hash = "sha256:a75c1ad05eedb1a3ff2a34a52a4f0836cfaa892e12796ba39a7732c82701eff4"},
-    {file = "lxml-4.6.4-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:47e955112ce64241fdb357acf0216081f9f3255b3ac9c502ca4b3323ec1ca558"},
-    {file = "lxml-4.6.4-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:20d7c8d90d449c6a353b15ee0459abae8395dbe59ad01e406ccbf30cd81c6f98"},
-    {file = "lxml-4.6.4-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:240db6f3228d26e3c6f4fad914b9ddaaf8707254e8b3efd564dc680c8ec3c264"},
-    {file = "lxml-4.6.4-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:351482da8dd028834028537f08724b1de22d40dcf3bb723b469446564f409074"},
-    {file = "lxml-4.6.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e678a643177c0e5ec947b645fa7bc84260dfb9b6bf8fb1fdd83008dfc2ca5928"},
-    {file = "lxml-4.6.4-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:15d0381feb56f08f78c5cc4fc385ddfe0bde1456e37f54a9322833371aec4060"},
-    {file = "lxml-4.6.4-cp36-cp36m-win32.whl", hash = "sha256:4ba74afe5ee5cb5e28d83b513a6e8f0875fda1dc1a9aea42cc0065f029160d2a"},
-    {file = "lxml-4.6.4-cp36-cp36m-win_amd64.whl", hash = "sha256:9c91a73971a922c13070fd8fa5a114c858251791ba2122a941e6aa781c713e44"},
-    {file = "lxml-4.6.4-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:6020c70ff695106bf80651953a23e37718ef1fee9abd060dcad8e32ab2dc13f3"},
-    {file = "lxml-4.6.4-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f5dd358536b8a964bf6bd48de038754c1609e72e5f17f5d21efe2dda17594dbf"},
-    {file = "lxml-4.6.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7ae7089d81fc502df4b217ad77f03c54039fe90dac0acbe70448d7e53bfbc57e"},
-    {file = "lxml-4.6.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:80d10d53d3184837445ff8562021bdd37f57c4cadacbf9d8726cc16220a00d54"},
-    {file = "lxml-4.6.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e95da348d57eb448d226a44b868ff2ca5786fbcbe417ac99ff62d0a7d724b9c7"},
-    {file = "lxml-4.6.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ffd65cfa33fed01735c82aca640fde4cc63f0414775cba11e06f84fae2085a6e"},
-    {file = "lxml-4.6.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:877666418598f6cb289546c77ff87590cfd212f903b522b0afa0b9fb73b3ccfb"},
-    {file = "lxml-4.6.4-cp37-cp37m-win32.whl", hash = "sha256:e91d24623e747eeb2d8121f4a94c6a7ad27dc48e747e2dc95bfe88632bd028a2"},
-    {file = "lxml-4.6.4-cp37-cp37m-win_amd64.whl", hash = "sha256:4ec9a80dd5704ecfde54319b6964368daf02848c8954d3bacb9b64d1c7659159"},
-    {file = "lxml-4.6.4-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:2901625f4a878a055d275beedc20ba9cb359cefc4386a967222fee29eb236038"},
-    {file = "lxml-4.6.4-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:b567178a74a2261345890eac66fbf394692a6e002709d329f28a673ca6042473"},
-    {file = "lxml-4.6.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4717123f7c11c81e0da69989e5a64079c3f402b0efeb4c6241db6c369d657bd8"},
-    {file = "lxml-4.6.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:cf201bf5594d1aab139fe53e3fca457e4f8204a5bbd65d48ab3b82a16f517868"},
-    {file = "lxml-4.6.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a77a3470ba37e11872c75ca95baf9b3312133a3d5a5dc720803b23098c653976"},
-    {file = "lxml-4.6.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:619c6d2b552bba00491e96c0518aad94002651c108a0f7364ff2d7798812c00e"},
-    {file = "lxml-4.6.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:601f0ab75538b280aaf1e720eb9d68d4fa104ac274e1e9e6971df488f4dcdb0f"},
-    {file = "lxml-4.6.4-cp38-cp38-win32.whl", hash = "sha256:75d3c5bbc0ddbad03bb68b9be638599f67e4b98ed3dcd0fec9f6f39e41ee96cb"},
-    {file = "lxml-4.6.4-cp38-cp38-win_amd64.whl", hash = "sha256:4341d135f5660db10184963d9c3418c3e28d7f868aaf8b11a323ebf85813f7f4"},
-    {file = "lxml-4.6.4-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:9db24803fa71e3305fe4a7812782b708da21a0b774b130dd1860cf40a6d7a3ee"},
-    {file = "lxml-4.6.4-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:afd60230ad9d8bcba005945ec3a343722f09e0b7f8ae804246e5d2cfc6bd71a6"},
-    {file = "lxml-4.6.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:0c15e1cd55055956e77b0732270f1c6005850696bc3ef3e03d01e78af84eaa42"},
-    {file = "lxml-4.6.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6d422b3c729737d8a39279a25fa156c983a56458f8b2f97661ee6fb22b80b1d6"},
-    {file = "lxml-4.6.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2eb90f6ec3c236ef2f1bb38aee7c0d23e77d423d395af6326e7cca637519a4cb"},
-    {file = "lxml-4.6.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:51a0e5d243687596f46e24e464121d4b232ad772e2d1785b2a2c0eb413c285d4"},
-    {file = "lxml-4.6.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d43bd68714049c84e297c005456a15ecdec818f7b5aa5868c8b0a865cfb78a44"},
-    {file = "lxml-4.6.4-cp39-cp39-win32.whl", hash = "sha256:ee9e4b07b0eba4b6a521509e9e1877476729c1243246b6959de697ebea739643"},
-    {file = "lxml-4.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:48eaac2991b3036175b42ee8d3c23f4cca13f2be8426bf29401a690ab58c88f4"},
-    {file = "lxml-4.6.4-pp37-pypy37_pp73-macosx_10_14_x86_64.whl", hash = "sha256:2b06a91cf7b8acea7793006e4ae50646cef0fe35ce5acd4f5cb1c77eb228e4a1"},
-    {file = "lxml-4.6.4-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:523f195948a1ba4f9f5b7294d83c6cd876547dc741820750a7e5e893a24bbe38"},
-    {file = "lxml-4.6.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:b0ca0ada9d3bc18bd6f611bd001a28abdd49ab9698bd6d717f7f5394c8e94628"},
-    {file = "lxml-4.6.4-pp38-pypy38_pp73-macosx_10_14_x86_64.whl", hash = "sha256:197b7cb7a753cf553a45115739afd8458464a28913da00f5c525063f94cd3f48"},
-    {file = "lxml-4.6.4-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:6298f5b42a26581206ef63fffa97c754245d329414108707c525512a5197f2ba"},
-    {file = "lxml-4.6.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0b12c95542f04d10cba46b3ff28ea52ea56995b78cf918f0b11b05e75812bb79"},
-    {file = "lxml-4.6.4.tar.gz", hash = "sha256:daf9bd1fee31f1c7a5928b3e1059e09a8d683ea58fb3ffc773b6c88cb8d1399c"},
-]
-markupsafe = [
-    {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"},
-    {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"},
-    {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"},
-    {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"},
-    {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"},
-    {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"},
-    {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"},
-    {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
-    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
-    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
-    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
-    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
-    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
-    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"},
-    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"},
-    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"},
-    {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
-    {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
-    {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
-    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"},
-    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"},
-    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
-    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
-    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
-    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"},
-    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"},
-    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"},
-    {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
-    {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
-    {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"},
-    {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
-    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
-    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
-    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
-    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
-    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
-    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"},
-    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"},
-    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"},
-    {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
-    {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
-    {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
-    {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"},
-    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"},
-    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"},
-    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
-    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
-    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
-    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"},
-    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"},
-    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"},
-    {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
-    {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
-    {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
-]
-matplotlib-inline = [
-    {file = "matplotlib-inline-0.1.3.tar.gz", hash = "sha256:a04bfba22e0d1395479f866853ec1ee28eea1485c1d69a6faf00dc3e24ff34ee"},
-    {file = "matplotlib_inline-0.1.3-py3-none-any.whl", hash = "sha256:aed605ba3b72462d64d475a21a9296f400a19c4f74a31b59103d2a99ffd5aa5c"},
-]
-mccabe = [
-    {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
-    {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
-]
-mypy = [
-    {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"},
-    {file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"},
-    {file = "mypy-0.910-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9"},
-    {file = "mypy-0.910-cp35-cp35m-win_amd64.whl", hash = "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e"},
-    {file = "mypy-0.910-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921"},
-    {file = "mypy-0.910-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6"},
-    {file = "mypy-0.910-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212"},
-    {file = "mypy-0.910-cp36-cp36m-win_amd64.whl", hash = "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885"},
-    {file = "mypy-0.910-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0"},
-    {file = "mypy-0.910-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de"},
-    {file = "mypy-0.910-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703"},
-    {file = "mypy-0.910-cp37-cp37m-win_amd64.whl", hash = "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a"},
-    {file = "mypy-0.910-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504"},
-    {file = "mypy-0.910-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9"},
-    {file = "mypy-0.910-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072"},
-    {file = "mypy-0.910-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811"},
-    {file = "mypy-0.910-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e"},
-    {file = "mypy-0.910-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b"},
-    {file = "mypy-0.910-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2"},
-    {file = "mypy-0.910-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97"},
-    {file = "mypy-0.910-cp39-cp39-win_amd64.whl", hash = "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8"},
-    {file = "mypy-0.910-py3-none-any.whl", hash = "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"},
-    {file = "mypy-0.910.tar.gz", hash = "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150"},
-]
-mypy-extensions = [
-    {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
-    {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
-]
-oauthlib = [
-    {file = "oauthlib-3.1.1-py2.py3-none-any.whl", hash = "sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc"},
-    {file = "oauthlib-3.1.1.tar.gz", hash = "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"},
-]
-outcome = [
-    {file = "outcome-1.1.0-py2.py3-none-any.whl", hash = "sha256:c7dd9375cfd3c12db9801d080a3b63d4b0a261aa996c4c13152380587288d958"},
-    {file = "outcome-1.1.0.tar.gz", hash = "sha256:e862f01d4e626e63e8f92c38d1f8d5546d3f9cce989263c521b2e7990d186967"},
-]
-packaging = [
-    {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
-    {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
-]
-parso = [
-    {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"},
-    {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"},
-]
-parsy = [
-    {file = "parsy-1.1.0-py3-none-any.whl", hash = "sha256:25bd5cea2954950ebbfdf71f8bdaf7fd45a5df5325fd36a1064be2204d9d4c94"},
-    {file = "parsy-1.1.0.tar.gz", hash = "sha256:36173ba01a5372c7a1b32352cc73a279a49198f52252adf1c8c1ed41d1f94e8d"},
-]
-pathspec = [
-    {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"},
-    {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"},
-]
-pbr = [
-    {file = "pbr-5.8.0-py2.py3-none-any.whl", hash = "sha256:176e8560eaf61e127817ef93d8a844803abb27a4d4637f0ff3bb783129be2e0a"},
-    {file = "pbr-5.8.0.tar.gz", hash = "sha256:672d8ebee84921862110f23fcec2acea191ef58543d34dfe9ef3d9f13c31cddf"},
-]
-persisting-theory = [
-    {file = "persisting-theory-0.2.1.tar.gz", hash = "sha256:00ff7dcc8f481ff75c770ca5797d968e8725b6df1f77fe0cf7d20fa1e5790c0a"},
-]
-pexpect = [
-    {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"},
-    {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"},
-]
-pg8000 = [
-    {file = "pg8000-1.23.0-py3-none-any.whl", hash = "sha256:e339f09f676629c0806c8dcc6fdc89ca6dba817e20702448c5172e85d133419d"},
-    {file = "pg8000-1.23.0.tar.gz", hash = "sha256:a413e00141342813a2ca47e8b7b0549ff338cca02bc819076b6d70f12d755c79"},
-]
-phonenumbers = [
-    {file = "phonenumbers-8.12.38-py2.py3-none-any.whl", hash = "sha256:95c8a30157323a73a4f9207792e3ed69b626b4c74d8fc44064f25e7fb56cfc94"},
-    {file = "phonenumbers-8.12.38.tar.gz", hash = "sha256:3cda1d1cea9a6801babf825e6c0f6a9776ea6d8a68b81b256178f8e5aa813344"},
-]
-pickleshare = [
-    {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"},
-    {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"},
-]
-pillow = [
-    {file = "Pillow-8.4.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:81f8d5c81e483a9442d72d182e1fb6dcb9723f289a57e8030811bac9ea3fef8d"},
-    {file = "Pillow-8.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f97cfb1e5a392d75dd8b9fd274d205404729923840ca94ca45a0af57e13dbe6"},
-    {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb9fc393f3c61f9054e1ed26e6fe912c7321af2f41ff49d3f83d05bacf22cc78"},
-    {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d82cdb63100ef5eedb8391732375e6d05993b765f72cb34311fab92103314649"},
-    {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc1afda735a8d109007164714e73771b499768b9bb5afcbbee9d0ff374b43f"},
-    {file = "Pillow-8.4.0-cp310-cp310-win32.whl", hash = "sha256:e3dacecfbeec9a33e932f00c6cd7996e62f53ad46fbe677577394aaa90ee419a"},
-    {file = "Pillow-8.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:620582db2a85b2df5f8a82ddeb52116560d7e5e6b055095f04ad828d1b0baa39"},
-    {file = "Pillow-8.4.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:1bc723b434fbc4ab50bb68e11e93ce5fb69866ad621e3c2c9bdb0cd70e345f55"},
-    {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72cbcfd54df6caf85cc35264c77ede902452d6df41166010262374155947460c"},
-    {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70ad9e5c6cb9b8487280a02c0ad8a51581dcbbe8484ce058477692a27c151c0a"},
-    {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25a49dc2e2f74e65efaa32b153527fc5ac98508d502fa46e74fa4fd678ed6645"},
-    {file = "Pillow-8.4.0-cp36-cp36m-win32.whl", hash = "sha256:93ce9e955cc95959df98505e4608ad98281fff037350d8c2671c9aa86bcf10a9"},
-    {file = "Pillow-8.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2e4440b8f00f504ee4b53fe30f4e381aae30b0568193be305256b1462216feff"},
-    {file = "Pillow-8.4.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8c803ac3c28bbc53763e6825746f05cc407b20e4a69d0122e526a582e3b5e153"},
-    {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8a17b5d948f4ceeceb66384727dde11b240736fddeda54ca740b9b8b1556b29"},
-    {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1394a6ad5abc838c5cd8a92c5a07535648cdf6d09e8e2d6df916dfa9ea86ead8"},
-    {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:792e5c12376594bfcb986ebf3855aa4b7c225754e9a9521298e460e92fb4a488"},
-    {file = "Pillow-8.4.0-cp37-cp37m-win32.whl", hash = "sha256:d99ec152570e4196772e7a8e4ba5320d2d27bf22fdf11743dd882936ed64305b"},
-    {file = "Pillow-8.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:7b7017b61bbcdd7f6363aeceb881e23c46583739cb69a3ab39cb384f6ec82e5b"},
-    {file = "Pillow-8.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:d89363f02658e253dbd171f7c3716a5d340a24ee82d38aab9183f7fdf0cdca49"},
-    {file = "Pillow-8.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a0956fdc5defc34462bb1c765ee88d933239f9a94bc37d132004775241a7585"},
-    {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b7bb9de00197fb4261825c15551adf7605cf14a80badf1761d61e59da347779"},
-    {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72b9e656e340447f827885b8d7a15fc8c4e68d410dc2297ef6787eec0f0ea409"},
-    {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5a4532a12314149d8b4e4ad8ff09dde7427731fcfa5917ff16d0291f13609df"},
-    {file = "Pillow-8.4.0-cp38-cp38-win32.whl", hash = "sha256:82aafa8d5eb68c8463b6e9baeb4f19043bb31fefc03eb7b216b51e6a9981ae09"},
-    {file = "Pillow-8.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:066f3999cb3b070a95c3652712cffa1a748cd02d60ad7b4e485c3748a04d9d76"},
-    {file = "Pillow-8.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:5503c86916d27c2e101b7f71c2ae2cddba01a2cf55b8395b0255fd33fa4d1f1a"},
-    {file = "Pillow-8.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4acc0985ddf39d1bc969a9220b51d94ed51695d455c228d8ac29fcdb25810e6e"},
-    {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b052a619a8bfcf26bd8b3f48f45283f9e977890263e4571f2393ed8898d331b"},
-    {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:493cb4e415f44cd601fcec11c99836f707bb714ab03f5ed46ac25713baf0ff20"},
-    {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8831cb7332eda5dc89b21a7bce7ef6ad305548820595033a4b03cf3091235ed"},
-    {file = "Pillow-8.4.0-cp39-cp39-win32.whl", hash = "sha256:5e9ac5f66616b87d4da618a20ab0a38324dbe88d8a39b55be8964eb520021e02"},
-    {file = "Pillow-8.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:3eb1ce5f65908556c2d8685a8f0a6e989d887ec4057326f6c22b24e8a172c66b"},
-    {file = "Pillow-8.4.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ddc4d832a0f0b4c52fff973a0d44b6c99839a9d016fe4e6a1cb8f3eea96479c2"},
-    {file = "Pillow-8.4.0-pp36-pypy36_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a3e5ddc44c14042f0844b8cf7d2cd455f6cc80fd7f5eefbe657292cf601d9ad"},
-    {file = "Pillow-8.4.0-pp36-pypy36_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c70e94281588ef053ae8998039610dbd71bc509e4acbc77ab59d7d2937b10698"},
-    {file = "Pillow-8.4.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:3862b7256046fcd950618ed22d1d60b842e3a40a48236a5498746f21189afbbc"},
-    {file = "Pillow-8.4.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4901622493f88b1a29bd30ec1a2f683782e57c3c16a2dbc7f2595ba01f639df"},
-    {file = "Pillow-8.4.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84c471a734240653a0ec91dec0996696eea227eafe72a33bd06c92697728046b"},
-    {file = "Pillow-8.4.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:244cf3b97802c34c41905d22810846802a3329ddcb93ccc432870243211c79fc"},
-    {file = "Pillow-8.4.0.tar.gz", hash = "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed"},
-]
-platformdirs = [
-    {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"},
-    {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"},
-]
-pluggy = [
-    {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
-    {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"},
-]
-prometheus-client = [
-    {file = "prometheus_client-0.12.0-py2.py3-none-any.whl", hash = "sha256:317453ebabff0a1b02df7f708efbab21e3489e7072b61cb6957230dd004a0af0"},
-    {file = "prometheus_client-0.12.0.tar.gz", hash = "sha256:1b12ba48cee33b9b0b9de64a1047cbd3c5f2d0ab6ebcead7ddda613a750ec3c5"},
-]
-prompt-toolkit = [
-    {file = "prompt_toolkit-3.0.23-py3-none-any.whl", hash = "sha256:5f29d62cb7a0ecacfa3d8ceea05a63cd22500543472d64298fc06ddda906b25d"},
-    {file = "prompt_toolkit-3.0.23.tar.gz", hash = "sha256:7053aba00895473cb357819358ef33f11aa97e4ac83d38efb123e5649ceeecaf"},
-]
-psutil = [
-    {file = "psutil-5.8.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64"},
-    {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0ae6f386d8d297177fd288be6e8d1afc05966878704dad9847719650e44fc49c"},
-    {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:12d844996d6c2b1d3881cfa6fa201fd635971869a9da945cf6756105af73d2df"},
-    {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:02b8292609b1f7fcb34173b25e48d0da8667bc85f81d7476584d889c6e0f2131"},
-    {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6ffe81843131ee0ffa02c317186ed1e759a145267d54fdef1bc4ea5f5931ab60"},
-    {file = "psutil-5.8.0-cp27-none-win32.whl", hash = "sha256:ea313bb02e5e25224e518e4352af4bf5e062755160f77e4b1767dd5ccb65f876"},
-    {file = "psutil-5.8.0-cp27-none-win_amd64.whl", hash = "sha256:5da29e394bdedd9144c7331192e20c1f79283fb03b06e6abd3a8ae45ffecee65"},
-    {file = "psutil-5.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:74fb2557d1430fff18ff0d72613c5ca30c45cdbfcddd6a5773e9fc1fe9364be8"},
-    {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:74f2d0be88db96ada78756cb3a3e1b107ce8ab79f65aa885f76d7664e56928f6"},
-    {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:99de3e8739258b3c3e8669cb9757c9a861b2a25ad0955f8e53ac662d66de61ac"},
-    {file = "psutil-5.8.0-cp36-cp36m-win32.whl", hash = "sha256:36b3b6c9e2a34b7d7fbae330a85bf72c30b1c827a4366a07443fc4b6270449e2"},
-    {file = "psutil-5.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:52de075468cd394ac98c66f9ca33b2f54ae1d9bff1ef6b67a212ee8f639ec06d"},
-    {file = "psutil-5.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c6a5fd10ce6b6344e616cf01cc5b849fa8103fbb5ba507b6b2dee4c11e84c935"},
-    {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:61f05864b42fedc0771d6d8e49c35f07efd209ade09a5afe6a5059e7bb7bf83d"},
-    {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0dd4465a039d343925cdc29023bb6960ccf4e74a65ad53e768403746a9207023"},
-    {file = "psutil-5.8.0-cp37-cp37m-win32.whl", hash = "sha256:1bff0d07e76114ec24ee32e7f7f8d0c4b0514b3fae93e3d2aaafd65d22502394"},
-    {file = "psutil-5.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563"},
-    {file = "psutil-5.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6223d07a1ae93f86451d0198a0c361032c4c93ebd4bf6d25e2fb3edfad9571ef"},
-    {file = "psutil-5.8.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d225cd8319aa1d3c85bf195c4e07d17d3cd68636b8fc97e6cf198f782f99af28"},
-    {file = "psutil-5.8.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:28ff7c95293ae74bf1ca1a79e8805fcde005c18a122ca983abf676ea3466362b"},
-    {file = "psutil-5.8.0-cp38-cp38-win32.whl", hash = "sha256:ce8b867423291cb65cfc6d9c4955ee9bfc1e21fe03bb50e177f2b957f1c2469d"},
-    {file = "psutil-5.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:90f31c34d25b1b3ed6c40cdd34ff122b1887a825297c017e4cbd6796dd8b672d"},
-    {file = "psutil-5.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6323d5d845c2785efb20aded4726636546b26d3b577aded22492908f7c1bdda7"},
-    {file = "psutil-5.8.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:245b5509968ac0bd179287d91210cd3f37add77dad385ef238b275bad35fa1c4"},
-    {file = "psutil-5.8.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b"},
-    {file = "psutil-5.8.0-cp39-cp39-win32.whl", hash = "sha256:ea372bcc129394485824ae3e3ddabe67dc0b118d262c568b4d2602a7070afdb0"},
-    {file = "psutil-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3"},
-    {file = "psutil-5.8.0.tar.gz", hash = "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6"},
-]
-psycopg2 = [
-    {file = "psycopg2-2.9.2-cp310-cp310-win32.whl", hash = "sha256:6796ac614412ce374587147150e56d03b7845c9e031b88aacdcadc880e81bb38"},
-    {file = "psycopg2-2.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:dfc32db6ce9ecc35a131320888b547199f79822b028934bb5b332f4169393e15"},
-    {file = "psycopg2-2.9.2-cp36-cp36m-win32.whl", hash = "sha256:77d09a79f9739b97099d2952bbbf18eaa4eaf825362387acbb9552ec1b3fa228"},
-    {file = "psycopg2-2.9.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f65cba7924363e0d2f416041b48ff69d559548f2cb168ff972c54e09e1e64db8"},
-    {file = "psycopg2-2.9.2-cp37-cp37m-win32.whl", hash = "sha256:b8816c6410fa08d2a022e4e38d128bae97c1855e176a00493d6ec62ccd606d57"},
-    {file = "psycopg2-2.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:26322c3f114de1f60c1b0febf8fdd595c221b4f624524178f515d07350a71bd1"},
-    {file = "psycopg2-2.9.2-cp38-cp38-win32.whl", hash = "sha256:77b9105ef37bc005b8ffbcb1ed6d8685bb0e8ce84773738aa56421a007ec5a7a"},
-    {file = "psycopg2-2.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:91c7fd0fe9e6c118e8ff5b665bc3445781d3615fa78e131d0b4f8c85e8ca9ec8"},
-    {file = "psycopg2-2.9.2-cp39-cp39-win32.whl", hash = "sha256:a761b60da0ecaf6a9866985bcde26327883ac3cdb90535ab68b8d784f02b05ef"},
-    {file = "psycopg2-2.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:fd7ddab7d6afee4e21c03c648c8b667b197104713e57ec404d5b74097af21e31"},
-    {file = "psycopg2-2.9.2.tar.gz", hash = "sha256:a84da9fa891848e0270e8e04dcca073bc9046441eeb47069f5c0e36783debbea"},
-]
-ptyprocess = [
-    {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
-    {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
-]
-py = [
-    {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"},
-    {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"},
-]
-pyasn1 = [
-    {file = "pyasn1-0.4.8-py2.py3-none-any.whl", hash = "sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d"},
-    {file = "pyasn1-0.4.8.tar.gz", hash = "sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba"},
-]
-pyasn1-modules = [
-    {file = "pyasn1-modules-0.2.8.tar.gz", hash = "sha256:905f84c712230b2c592c19470d3ca8d552de726050d1d1716282a1f6146be65e"},
-    {file = "pyasn1_modules-0.2.8-py2.py3-none-any.whl", hash = "sha256:a50b808ffeb97cb3601dd25981f6b016cbb3d31fbf57a8b8a87428e6158d0c74"},
-]
-pycodestyle = [
-    {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"},
-    {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"},
-]
-pycparser = [
-    {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"},
-    {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
-]
-pycryptodome = [
-    {file = "pycryptodome-3.11.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ffd0cac13ff41f2d15ed39dc6ba1d2ad88dd2905d656c33d8235852f5d6151fd"},
-    {file = "pycryptodome-3.11.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:ead516e03dfe062aefeafe4a29445a6449b0fc43bc8cb30194b2754917a63798"},
-    {file = "pycryptodome-3.11.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:4ce6b09547bf2c7cede3a017f79502eaed3e819c13cdb3cb357aea1b004e4cc6"},
-    {file = "pycryptodome-3.11.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:014c758af7fa38cab85b357a496b76f4fc9dda1f731eb28358d66fef7ad4a3e1"},
-    {file = "pycryptodome-3.11.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a843350d08c3d22f6c09c2f17f020d8dcfa59496165d7425a3fba0045543dda7"},
-    {file = "pycryptodome-3.11.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:53989477044be41fa4a63da09d5038c2a34b2f4554cfea2e3933b17186ee9e19"},
-    {file = "pycryptodome-3.11.0-cp27-cp27m-win32.whl", hash = "sha256:f9bad2220b80b4ed74f089db012ab5ab5419143a33fad6c8aedcc2a9341eac70"},
-    {file = "pycryptodome-3.11.0-cp27-cp27m-win_amd64.whl", hash = "sha256:3c7ed5b07274535979c730daf5817db5e983ea80b04c22579eee8da4ca3ae4f8"},
-    {file = "pycryptodome-3.11.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:8f3a60926be78422e662b0d0b18351b426ce27657101c8a50bad80300de6a701"},
-    {file = "pycryptodome-3.11.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:fce7e22d96030b35345637c563246c24d4513bd3b413e1c40293114837ab8912"},
-    {file = "pycryptodome-3.11.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:bc3c61ff92efdcc14af4a7b81da71d849c9acee51d8fd8ac9841a7620140d6c6"},
-    {file = "pycryptodome-3.11.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:b33c9b3d1327d821e28e9cc3a6512c14f8b17570ddb4cfb9a52247ed0fcc5d8b"},
-    {file = "pycryptodome-3.11.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:75e78360d1dd6d02eb288fd8275bb4d147d6e3f5337935c096d11dba1fa84748"},
-    {file = "pycryptodome-3.11.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:621a90147a5e255fdc2a0fec2d56626b76b5d72ea9e60164c9a5a8976d45b0c9"},
-    {file = "pycryptodome-3.11.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:0ca7a6b4fc1f9fafe990b95c8cda89099797e2cfbf40e55607f2f2f5a3355dcb"},
-    {file = "pycryptodome-3.11.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:b59bf823cfafde8ef1105d8984f26d1694dff165adb7198b12e3e068d7999b15"},
-    {file = "pycryptodome-3.11.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:ce81b9c6aaa0f920e2ab05eb2b9f4ccd102e3016b2f37125593b16a83a4b0cc2"},
-    {file = "pycryptodome-3.11.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:ae29fcd56152f417bfba50a36a56a7a5f9fb74ff80bab98704cac704de6568ab"},
-    {file = "pycryptodome-3.11.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:ae31cb874f6f0cedbed457c6374e7e54d7ed45c1a4e11a65a9c80968da90a650"},
-    {file = "pycryptodome-3.11.0-cp35-abi3-win32.whl", hash = "sha256:6db1f9fa1f52226621905f004278ce7bd90c8f5363ffd5d7ab3755363d98549a"},
-    {file = "pycryptodome-3.11.0-cp35-abi3-win_amd64.whl", hash = "sha256:d7e5f6f692421e5219aa3b545eb0cffd832cd589a4b9dcd4a5eb4260e2c0d68a"},
-    {file = "pycryptodome-3.11.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:da796e9221dda61a0019d01742337eb8a322de8598b678a4344ca0a436380315"},
-    {file = "pycryptodome-3.11.0-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:ed45ef92d21db33685b789de2c015e9d9a18a74760a8df1fc152faee88cdf741"},
-    {file = "pycryptodome-3.11.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:4169ed515742425ff21e4bd3fabbb6994ffb64434472fb72230019bdfa36b939"},
-    {file = "pycryptodome-3.11.0-pp27-pypy_73-win32.whl", hash = "sha256:f19edd42368e9057c39492947bb99570dc927123e210008f2af7cf9b505c6892"},
-    {file = "pycryptodome-3.11.0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:06162fcfed2f9deee8383fd59eaeabc7b7ffc3af50d3fad4000032deb8f700b0"},
-    {file = "pycryptodome-3.11.0-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:6eda8a3157c91ba60b26a07bedd6c44ab8bda6cd79b6b5ea9744ba62c39b7b1e"},
-    {file = "pycryptodome-3.11.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:7ff701fc283412e651eaab4319b3cd4eaa0827e94569cd37ee9075d5c05fe655"},
-    {file = "pycryptodome-3.11.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:2a4bcc8a9977fee0979079cd33a9e9f0d3ddba5660d35ffe874cf84f1dd399d2"},
-    {file = "pycryptodome-3.11.0.tar.gz", hash = "sha256:428096bbf7a77e207f418dfd4d7c284df8ade81d2dc80f010e92753a3e406ad0"},
-]
-pydocstyle = [
-    {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"},
-    {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"},
-]
-pyflakes = [
-    {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"},
-    {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"},
-]
-pygments = [
-    {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"},
-    {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"},
-]
-pyjwt = [
-    {file = "PyJWT-2.3.0-py3-none-any.whl", hash = "sha256:e0c4bb8d9f0af0c7f5b1ec4c5036309617d03d56932877f2f7a0beeb5318322f"},
-    {file = "PyJWT-2.3.0.tar.gz", hash = "sha256:b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41"},
-]
-pyopenssl = [
-    {file = "pyOpenSSL-21.0.0-py2.py3-none-any.whl", hash = "sha256:8935bd4920ab9abfebb07c41a4f58296407ed77f04bd1a92914044b848ba1ed6"},
-    {file = "pyOpenSSL-21.0.0.tar.gz", hash = "sha256:5e2d8c5e46d0d865ae933bef5230090bdaf5506281e9eec60fa250ee80600cb3"},
-]
-pyparsing = [
-    {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"},
-    {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"},
-]
-pytest = [
-    {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"},
-    {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"},
-]
-pytest-cov = [
-    {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"},
-    {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"},
-]
-pytest-django = [
-    {file = "pytest-django-4.5.0.tar.gz", hash = "sha256:4b1120c364404cfa9f54e2229b5c39151821bb17819e4bcf357e0f62a3e925a0"},
-    {file = "pytest_django-4.5.0-py3-none-any.whl", hash = "sha256:10cb6e5baacd56ca1f0134ce448b050c31824ba4e480eb7e0fa3832f3a0f8b4c"},
-]
-pytest-django-testing-postgresql = [
-    {file = "pytest-django-testing-postgresql-0.1.post0.tar.gz", hash = "sha256:78b0c58930084cb4393407b2e5a2a3b8734c627b841ecef7d62d39bbfb8e8a45"},
-    {file = "pytest_django_testing_postgresql-0.1.post0-py3-none-any.whl", hash = "sha256:78e52e3d1b0ef5f906d5d69247dd6ac7dfb10d840bd81abab92f3f8c30872cd3"},
-]
-pytest-sugar = [
-    {file = "pytest-sugar-0.9.4.tar.gz", hash = "sha256:b1b2186b0a72aada6859bea2a5764145e3aaa2c1cfbb23c3a19b5f7b697563d3"},
-]
-python-crontab = [
-    {file = "python-crontab-2.6.0.tar.gz", hash = "sha256:1e35ed7a3cdc3100545b43e196d34754e6551e7f95e4caebbe0e1c0ca41c2f1b"},
-]
-python-dateutil = [
-    {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
-    {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
-]
-python-gnupg = [
-    {file = "python-gnupg-0.4.8.tar.gz", hash = "sha256:b64de1ae5cedf872b437201a566fa2c62ce0c95ea2e30177eb53aee1258507d7"},
-    {file = "python_gnupg-0.4.8-py2.py3-none-any.whl", hash = "sha256:93a521501d6c2785d96b190aec7125ba89c1c2fe708b0c98af3fb32b59026ab8"},
-]
-python-ldap = [
-    {file = "python-ldap-3.4.0.tar.gz", hash = "sha256:60464c8fc25e71e0fd40449a24eae482dcd0fb7fcf823e7de627a6525b3e0d12"},
-]
-python3-openid = [
-    {file = "python3-openid-3.2.0.tar.gz", hash = "sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf"},
-    {file = "python3_openid-3.2.0-py3-none-any.whl", hash = "sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b"},
-]
-pytz = [
-    {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"},
-    {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"},
-]
-pyyaml = [
-    {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
-    {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
-    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"},
-    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"},
-    {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
-    {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
-    {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
-    {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
-    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
-    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
-    {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"},
-    {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"},
-    {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"},
-    {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"},
-    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"},
-    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"},
-    {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"},
-    {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"},
-    {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"},
-    {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"},
-    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"},
-    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"},
-    {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"},
-    {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"},
-    {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"},
-    {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"},
-    {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"},
-    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"},
-    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"},
-    {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"},
-    {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"},
-    {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
-    {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
-]
-qrcode = [
-    {file = "qrcode-6.1-py2.py3-none-any.whl", hash = "sha256:3996ee560fc39532910603704c82980ff6d4d5d629f9c3f25f34174ce8606cf5"},
-    {file = "qrcode-6.1.tar.gz", hash = "sha256:505253854f607f2abf4d16092c61d4e9d511a3b4392e60bff957a68592b04369"},
-]
-redis = [
-    {file = "redis-3.5.3-py2.py3-none-any.whl", hash = "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"},
-    {file = "redis-3.5.3.tar.gz", hash = "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2"},
-]
-regex = [
-    {file = "regex-2021.11.10-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9345b6f7ee578bad8e475129ed40123d265464c4cfead6c261fd60fc9de00bcf"},
-    {file = "regex-2021.11.10-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:416c5f1a188c91e3eb41e9c8787288e707f7d2ebe66e0a6563af280d9b68478f"},
-    {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0538c43565ee6e703d3a7c3bdfe4037a5209250e8502c98f20fea6f5fdf2965"},
-    {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ee1227cf08b6716c85504aebc49ac827eb88fcc6e51564f010f11a406c0a667"},
-    {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6650f16365f1924d6014d2ea770bde8555b4a39dc9576abb95e3cd1ff0263b36"},
-    {file = "regex-2021.11.10-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:30ab804ea73972049b7a2a5c62d97687d69b5a60a67adca07eb73a0ddbc9e29f"},
-    {file = "regex-2021.11.10-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68a067c11463de2a37157930d8b153005085e42bcb7ad9ca562d77ba7d1404e0"},
-    {file = "regex-2021.11.10-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:162abfd74e88001d20cb73ceaffbfe601469923e875caf9118333b1a4aaafdc4"},
-    {file = "regex-2021.11.10-cp310-cp310-win32.whl", hash = "sha256:98ba568e8ae26beb726aeea2273053c717641933836568c2a0278a84987b2a1a"},
-    {file = "regex-2021.11.10-cp310-cp310-win_amd64.whl", hash = "sha256:780b48456a0f0ba4d390e8b5f7c661fdd218934388cde1a974010a965e200e12"},
-    {file = "regex-2021.11.10-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:dba70f30fd81f8ce6d32ddeef37d91c8948e5d5a4c63242d16a2b2df8143aafc"},
-    {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1f54b9b4b6c53369f40028d2dd07a8c374583417ee6ec0ea304e710a20f80a0"},
-    {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fbb9dc00e39f3e6c0ef48edee202f9520dafb233e8b51b06b8428cfcb92abd30"},
-    {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:666abff54e474d28ff42756d94544cdfd42e2ee97065857413b72e8a2d6a6345"},
-    {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5537f71b6d646f7f5f340562ec4c77b6e1c915f8baae822ea0b7e46c1f09b733"},
-    {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2e07c6a26ed4bea91b897ee2b0835c21716d9a469a96c3e878dc5f8c55bb23"},
-    {file = "regex-2021.11.10-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ca5f18a75e1256ce07494e245cdb146f5a9267d3c702ebf9b65c7f8bd843431e"},
-    {file = "regex-2021.11.10-cp36-cp36m-win32.whl", hash = "sha256:93a5051fcf5fad72de73b96f07d30bc29665697fb8ecdfbc474f3452c78adcf4"},
-    {file = "regex-2021.11.10-cp36-cp36m-win_amd64.whl", hash = "sha256:b483c9d00a565633c87abd0aaf27eb5016de23fed952e054ecc19ce32f6a9e7e"},
-    {file = "regex-2021.11.10-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fff55f3ce50a3ff63ec8e2a8d3dd924f1941b250b0aac3d3d42b687eeff07a8e"},
-    {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32d2a2b02ccbef10145df9135751abea1f9f076e67a4e261b05f24b94219e36"},
-    {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53db2c6be8a2710b359bfd3d3aa17ba38f8aa72a82309a12ae99d3c0c3dcd74d"},
-    {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2207ae4f64ad3af399e2d30dde66f0b36ae5c3129b52885f1bffc2f05ec505c8"},
-    {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5ca078bb666c4a9d1287a379fe617a6dccd18c3e8a7e6c7e1eb8974330c626a"},
-    {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd33eb9bdcfbabab3459c9ee651d94c842bc8a05fabc95edf4ee0c15a072495e"},
-    {file = "regex-2021.11.10-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05b7d6d7e64efe309972adab77fc2af8907bb93217ec60aa9fe12a0dad35874f"},
-    {file = "regex-2021.11.10-cp37-cp37m-win32.whl", hash = "sha256:e71255ba42567d34a13c03968736c5d39bb4a97ce98188fafb27ce981115beec"},
-    {file = "regex-2021.11.10-cp37-cp37m-win_amd64.whl", hash = "sha256:07856afef5ffcc052e7eccf3213317fbb94e4a5cd8177a2caa69c980657b3cb4"},
-    {file = "regex-2021.11.10-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba05430e819e58544e840a68b03b28b6d328aff2e41579037e8bab7653b37d83"},
-    {file = "regex-2021.11.10-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7f301b11b9d214f83ddaf689181051e7f48905568b0c7017c04c06dfd065e244"},
-    {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aaa4e0705ef2b73dd8e36eeb4c868f80f8393f5f4d855e94025ce7ad8525f50"},
-    {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:788aef3549f1924d5c38263104dae7395bf020a42776d5ec5ea2b0d3d85d6646"},
-    {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f8af619e3be812a2059b212064ea7a640aff0568d972cd1b9e920837469eb3cb"},
-    {file = "regex-2021.11.10-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85bfa6a5413be0ee6c5c4a663668a2cad2cbecdee367630d097d7823041bdeec"},
-    {file = "regex-2021.11.10-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f23222527b307970e383433daec128d769ff778d9b29343fb3496472dc20dabe"},
-    {file = "regex-2021.11.10-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:da1a90c1ddb7531b1d5ff1e171b4ee61f6345119be7351104b67ff413843fe94"},
-    {file = "regex-2021.11.10-cp38-cp38-win32.whl", hash = "sha256:0617383e2fe465732af4509e61648b77cbe3aee68b6ac8c0b6fe934db90be5cc"},
-    {file = "regex-2021.11.10-cp38-cp38-win_amd64.whl", hash = "sha256:a3feefd5e95871872673b08636f96b61ebef62971eab044f5124fb4dea39919d"},
-    {file = "regex-2021.11.10-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7f325be2804246a75a4f45c72d4ce80d2443ab815063cdf70ee8fb2ca59ee1b"},
-    {file = "regex-2021.11.10-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:537ca6a3586931b16a85ac38c08cc48f10fc870a5b25e51794c74df843e9966d"},
-    {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eef2afb0fd1747f33f1ee3e209bce1ed582d1896b240ccc5e2697e3275f037c7"},
-    {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:432bd15d40ed835a51617521d60d0125867f7b88acf653e4ed994a1f8e4995dc"},
-    {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b43c2b8a330a490daaef5a47ab114935002b13b3f9dc5da56d5322ff218eeadb"},
-    {file = "regex-2021.11.10-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:962b9a917dd7ceacbe5cd424556914cb0d636001e393b43dc886ba31d2a1e449"},
-    {file = "regex-2021.11.10-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fa8c626d6441e2d04b6ee703ef2d1e17608ad44c7cb75258c09dd42bacdfc64b"},
-    {file = "regex-2021.11.10-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3c5fb32cc6077abad3bbf0323067636d93307c9fa93e072771cf9a64d1c0f3ef"},
-    {file = "regex-2021.11.10-cp39-cp39-win32.whl", hash = "sha256:3b5df18db1fccd66de15aa59c41e4f853b5df7550723d26aa6cb7f40e5d9da5a"},
-    {file = "regex-2021.11.10-cp39-cp39-win_amd64.whl", hash = "sha256:83ee89483672b11f8952b158640d0c0ff02dc43d9cb1b70c1564b49abe92ce29"},
-    {file = "regex-2021.11.10.tar.gz", hash = "sha256:f341ee2df0999bfdf7a95e448075effe0db212a59387de1a70690e4acb03d4c6"},
-]
-reportlab = [
-    {file = "reportlab-3.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:169d7017687c60ddf7690cb349e24ae10d51480d93babc6f81d851aea8a01a25"},
-    {file = "reportlab-3.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:af350308e8b8720ca6db7763b56e96b37976576c1f8b0069764074dc41a19254"},
-    {file = "reportlab-3.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0cc8308678a8136481572463e5ac92a145d2fb487aa22c842347af8a063d50a"},
-    {file = "reportlab-3.6.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d7145d010d561c502e51795adb2e4f7534f28f12e32db0b0694081c6173c8b27"},
-    {file = "reportlab-3.6.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae3959094b48bdae729e838d84156d31691f0bd17c640b8b10ff0fdbbc11c031"},
-    {file = "reportlab-3.6.3-cp310-cp310-win32.whl", hash = "sha256:2fa30756c2d5d5e11321114636b0cbeffdf59ffdbcf705b902057ad3801d0d37"},
-    {file = "reportlab-3.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:7e51f0195e602c207edcf3a768a6005a10b3db786539b74eb99afe6c9fd5656a"},
-    {file = "reportlab-3.6.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:7cb6a4ea5cfc08669c0ba66e900cf5c598c33fdb0e3e4ebadf892ff703d594f6"},
-    {file = "reportlab-3.6.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b214321e2e16209a68a4cdbe3391379845cd0da434362cf3c6d2d1053146fe"},
-    {file = "reportlab-3.6.3-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:68de7f3d3511ea45b9af6e9e4a6b6d89a86d1fe59f41583fb55e6c810ebf23d9"},
-    {file = "reportlab-3.6.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c79f89c277f49dfd2829b9a046b20c12cd46fe69f474e4dca349e731a2397021"},
-    {file = "reportlab-3.6.3-cp36-cp36m-win32.whl", hash = "sha256:c29e7cde992c56687c6f68053dea4aa3beb0d406df28738e95950d88deb38109"},
-    {file = "reportlab-3.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:1055380b2dc73d5143a62451f81b2a739f605dae0e107a764efc0618293837c1"},
-    {file = "reportlab-3.6.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d851fecf4bb6484d4038a402430f30294911aa116686624d1e79b9e3cdb1ed2c"},
-    {file = "reportlab-3.6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4e0abfa0daf0c48a6bffc46c51b2e5f6272a1d57593c51e7bc855272f763130"},
-    {file = "reportlab-3.6.3-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d94a20b7f2aad166ffe74a77bff02d82a82c514dba6d3e431fdb14675db5c383"},
-    {file = "reportlab-3.6.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b75a4f634e8ba89cc22ff58b9bdfd01a239b2e2fc85d086d1a2d70d08167c443"},
-    {file = "reportlab-3.6.3-cp37-cp37m-win32.whl", hash = "sha256:27ce3e8869c3f22d9d7a8092858649601e76b86e2f3032df39566343b908d929"},
-    {file = "reportlab-3.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:5785aab3b57fe4794147da8908d411c3abf900ac2bc5b0c39782205a1d8c5b51"},
-    {file = "reportlab-3.6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d35cc6ae73a25b2e75b23e60d3dca0e5153dff97d9e22b28ac82712c79656fd9"},
-    {file = "reportlab-3.6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:720335d2462ed8ad5ad6ea5da509a9289fab7a78b0cdcdfd56fef5398435119d"},
-    {file = "reportlab-3.6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcbf4bcf0917cb50964a9c47198e802791eae2581e7788f43da8f50ed5744d8a"},
-    {file = "reportlab-3.6.3-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50e62429a3487722b4d7621453efc4cbeed80f3b608cad98ef8f06d191a8d0a9"},
-    {file = "reportlab-3.6.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d57b5374a44aab9f3898c57ed3c2e6ba159878a2a37f36ddd154adf6f4b3a5c0"},
-    {file = "reportlab-3.6.3-cp38-cp38-win32.whl", hash = "sha256:3b85b7172b9b4ab0ae74fe0e99d9186ff04104d9158c980391f4e0a54b67f3a8"},
-    {file = "reportlab-3.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:f2c0977a39bea423f6a0b53faaabef3b2f8602b1d3e09c57f6952d60ad363119"},
-    {file = "reportlab-3.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3d5d9997b91ec7f2d1a2b9928ab20503cf70e8c056b445d0bc7ae29946c13623"},
-    {file = "reportlab-3.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5d1280c9df17a5d36213b88d66f45a9475005ab39d79b6e9e98893a02a5a033d"},
-    {file = "reportlab-3.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5b56d9912f148cca4fdc0d6c805a56931bb1a4e59e899db92a3531e78f2a9de"},
-    {file = "reportlab-3.6.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c166a6c168232948e9b100dcab1b88a4b21efe0aa14c5c18bafd28fffffb4576"},
-    {file = "reportlab-3.6.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efdb2999e1b19a16d5535aa70b5ec5f3ad003e1d366da566f23231ff81400aa"},
-    {file = "reportlab-3.6.3-cp39-cp39-win32.whl", hash = "sha256:0c2f479df4eb447ea0757bf60b1ac0d7ddb9980335f4f94975ee6d95bda5b8f8"},
-    {file = "reportlab-3.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:bb8c380bceb3623f39a96b6bc8cf8db75857fea3f67900391763b5caee23ffa3"},
-    {file = "reportlab-3.6.3.tar.gz", hash = "sha256:be4f05230eb17b9c9c61a180ab0c89c30112da2823c77807a2a5ddba19365865"},
-]
-requests = [
-    {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"},
-    {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"},
-]
-requests-oauthlib = [
-    {file = "requests-oauthlib-1.3.0.tar.gz", hash = "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"},
-    {file = "requests_oauthlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d"},
-]
-restructuredtext-lint = [
-    {file = "restructuredtext_lint-1.3.2.tar.gz", hash = "sha256:d3b10a1fe2ecac537e51ae6d151b223b78de9fafdd50e5eb6b08c243df173c80"},
-]
-"ruamel.yaml" = [
-    {file = "ruamel.yaml-0.17.17-py3-none-any.whl", hash = "sha256:9af3ec5d7f8065582f3aa841305465025d0afd26c5fb54e15b964e11838fc74f"},
-    {file = "ruamel.yaml-0.17.17.tar.gz", hash = "sha256:9751de4cbb57d4bfbf8fc394e125ed4a2f170fbff3dc3d78abf50be85924f8be"},
-]
-"ruamel.yaml.clib" = [
-    {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751"},
-    {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527"},
-    {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win32.whl", hash = "sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5"},
-    {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win_amd64.whl", hash = "sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c"},
-    {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502"},
-    {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78"},
-    {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win32.whl", hash = "sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94"},
-    {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win_amd64.whl", hash = "sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468"},
-    {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd"},
-    {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99"},
-    {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win32.whl", hash = "sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb"},
-    {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win_amd64.whl", hash = "sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe"},
-    {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233"},
-    {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84"},
-    {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win32.whl", hash = "sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b"},
-    {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win_amd64.whl", hash = "sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277"},
-    {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed"},
-    {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0"},
-    {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win32.whl", hash = "sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104"},
-    {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win_amd64.whl", hash = "sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7"},
-    {file = "ruamel.yaml.clib-0.2.6.tar.gz", hash = "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd"},
-]
-rules = [
-    {file = "rules-3.0-py2.py3-none-any.whl", hash = "sha256:8194937b537c8a384eafe21750f1d396e1aecdc833e7d06808a5b805ae42a852"},
-    {file = "rules-3.0.tar.gz", hash = "sha256:9141e2fdb7f300fcb59f2f06619fe4ff52bb846eb112ba8c30444f971d6af05e"},
-]
-s3transfer = [
-    {file = "s3transfer-0.5.0-py3-none-any.whl", hash = "sha256:9c1dc369814391a6bda20ebbf4b70a0f34630592c9aa520856bf384916af2803"},
-    {file = "s3transfer-0.5.0.tar.gz", hash = "sha256:50ed823e1dc5868ad40c8dc92072f757aa0e653a192845c94a3b676f4a62da4c"},
-]
-safety = [
-    {file = "safety-1.10.3-py2.py3-none-any.whl", hash = "sha256:5f802ad5df5614f9622d8d71fedec2757099705c2356f862847c58c6dfe13e84"},
-    {file = "safety-1.10.3.tar.gz", hash = "sha256:30e394d02a20ac49b7f65292d19d38fa927a8f9582cdfd3ad1adbbc66c641ad5"},
-]
-scramp = [
-    {file = "scramp-1.4.1-py3-none-any.whl", hash = "sha256:93c9cc2ffe54a451e02981c07a5a23cbd830701102789939cfb4ff91efd6ca8c"},
-    {file = "scramp-1.4.1.tar.gz", hash = "sha256:f964801077be9be2a1416ffe255d2d78834b3d9d5c8ce5d28f76a856f209f70e"},
-]
-selenium = [
-    {file = "selenium-4.1.0-py3-none-any.whl", hash = "sha256:27e7b64df961d609f3d57237caa0df123abbbe22d038f2ec9e332fb90ec1a939"},
-]
-sentry-sdk = [
-    {file = "sentry-sdk-1.5.0.tar.gz", hash = "sha256:789a11a87ca02491896e121efdd64e8fd93327b69e8f2f7d42f03e2569648e88"},
-    {file = "sentry_sdk-1.5.0-py2.py3-none-any.whl", hash = "sha256:0db297ab32e095705c20f742c3a5dac62fe15c4318681884053d0898e5abb2f6"},
-]
-six = [
-    {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
-    {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
-]
-smmap = [
-    {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"},
-    {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"},
-]
-sniffio = [
-    {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"},
-    {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"},
-]
-snowballstemmer = [
-    {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"},
-    {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
-]
-sortedcontainers = [
-    {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"},
-    {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"},
-]
-soupsieve = [
-    {file = "soupsieve-2.3.1-py3-none-any.whl", hash = "sha256:1a3cca2617c6b38c0343ed661b1fa5de5637f257d4fe22bd9f1338010a1efefb"},
-    {file = "soupsieve-2.3.1.tar.gz", hash = "sha256:b8d49b1cd4f037c7082a9683dfa1801aa2597fb11c3a1155b7a5b94829b4f1f9"},
-]
-spdx-license-list = [
-    {file = "spdx_license_list-0.5.2-py3-none-any.whl", hash = "sha256:1b338470c7b403dbecceca563a316382c7977516128ca6c1e8f7078e3ed6e7b0"},
-    {file = "spdx_license_list-0.5.2.tar.gz", hash = "sha256:952996f72ab807972dc2278bb9b91e5294767211e51f09aad9c0e2ff5b82a31b"},
-]
-sphinx = [
-    {file = "Sphinx-3.5.4-py3-none-any.whl", hash = "sha256:2320d4e994a191f4b4be27da514e46b3d6b420f2ff895d064f52415d342461e8"},
-    {file = "Sphinx-3.5.4.tar.gz", hash = "sha256:19010b7b9fa0dc7756a6e105b2aacd3a80f798af3c25c273be64d7beeb482cb1"},
-]
-sphinx-autodoc-typehints = [
-    {file = "sphinx-autodoc-typehints-1.12.0.tar.gz", hash = "sha256:193617d9dbe0847281b1399d369e74e34cd959c82e02c7efde077fca908a9f52"},
-    {file = "sphinx_autodoc_typehints-1.12.0-py3-none-any.whl", hash = "sha256:5e81776ec422dd168d688ab60f034fccfafbcd94329e9537712c93003bddc04a"},
-]
-sphinx-materialdesign-theme = [
-    {file = "sphinx_materialdesign_theme-0.1.11.tar.gz", hash = "sha256:6e9dae4c6e5ba23c0657a94c1cf65f64be9c8bc1594a6fb41815f7daa3326aa9"},
-]
-sphinxcontrib-applehelp = [
-    {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"},
-]
-sphinxcontrib-devhelp = [
-    {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"},
-]
-sphinxcontrib-django = [
-    {file = "sphinxcontrib-django-0.5.1.tar.gz", hash = "sha256:3b48a9067d8db4713d47e3a4160af10288d02d448c866d1b44b001adbe74cc1e"},
-    {file = "sphinxcontrib_django-0.5.1-py2.py3-none-any.whl", hash = "sha256:73ef7fdbf2ed6d4f35b7ae709032bd5ac493d93cedd0624ea7b51bf5fce41267"},
-]
-sphinxcontrib-htmlhelp = [
-    {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"},
-]
-sphinxcontrib-jsmath = [
-    {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"},
-]
-sphinxcontrib-qthelp = [
-    {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"},
-]
-sphinxcontrib-serializinghtml = [
-    {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"},
-]
-sqlparse = [
-    {file = "sqlparse-0.4.2-py3-none-any.whl", hash = "sha256:48719e356bb8b42991bdbb1e8b83223757b93789c00910a616a071910ca4a64d"},
-    {file = "sqlparse-0.4.2.tar.gz", hash = "sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae"},
-]
-stevedore = [
-    {file = "stevedore-3.5.0-py3-none-any.whl", hash = "sha256:a547de73308fd7e90075bb4d301405bebf705292fa90a90fc3bcf9133f58616c"},
-    {file = "stevedore-3.5.0.tar.gz", hash = "sha256:f40253887d8712eaa2bb0ea3830374416736dc8ec0e22f5a65092c1174c44335"},
-]
-svglib = [
-    {file = "svglib-1.1.0.tar.gz", hash = "sha256:520ee5290ee2ebeebd20ca0d7d995c08c903b364fcf515826bab43a1288d422e"},
-]
-termcolor = [
-    {file = "termcolor-1.1.0.tar.gz", hash = "sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"},
-]
-testfixtures = [
-    {file = "testfixtures-6.18.3-py2.py3-none-any.whl", hash = "sha256:6ddb7f56a123e1a9339f130a200359092bd0a6455e31838d6c477e8729bb7763"},
-    {file = "testfixtures-6.18.3.tar.gz", hash = "sha256:2600100ae96ffd082334b378e355550fef8b4a529a6fa4c34f47130905c7426d"},
-]
-"testing.common.database" = [
-    {file = "testing.common.database-2.0.3-py2.py3-none-any.whl", hash = "sha256:e3ed492bf480a87f271f74c53b262caf5d85c8bc09989a8f534fa2283ec52492"},
-    {file = "testing.common.database-2.0.3.tar.gz", hash = "sha256:965d80b2985315325dc358c3061b174a712f4d4d5bf6a80b58b11f9a1dd86d73"},
-]
-"testing.postgresql" = [
-    {file = "testing.postgresql-1.3.0-py2.py3-none-any.whl", hash = "sha256:1b41daeb98dfc8cd4a584bb91e8f5f4ab182993870f95257afe5f1ba6151a598"},
-    {file = "testing.postgresql-1.3.0.tar.gz", hash = "sha256:8e1a69760369a7a8ffe63a66b6d95a5cd82db2fb976e4a8f85ffd24fbfc447d8"},
-]
-tinycss2 = [
-    {file = "tinycss2-1.1.1-py3-none-any.whl", hash = "sha256:fe794ceaadfe3cf3e686b22155d0da5780dd0e273471a51846d0a02bc204fec8"},
-    {file = "tinycss2-1.1.1.tar.gz", hash = "sha256:b2e44dd8883c360c35dd0d1b5aad0b610e5156c2cb3b33434634e539ead9d8bf"},
-]
-toml = [
-    {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
-    {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
-]
-tomli = [
-    {file = "tomli-1.2.2-py3-none-any.whl", hash = "sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"},
-    {file = "tomli-1.2.2.tar.gz", hash = "sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee"},
-]
-traitlets = [
-    {file = "traitlets-5.1.1-py3-none-any.whl", hash = "sha256:2d313cc50a42cd6c277e7d7dc8d4d7fedd06a2c215f78766ae7b1a66277e0033"},
-    {file = "traitlets-5.1.1.tar.gz", hash = "sha256:059f456c5a7c1c82b98c2e8c799f39c9b8128f6d0d46941ee118daace9eb70c7"},
-]
-trio = [
-    {file = "trio-0.19.0-py3-none-any.whl", hash = "sha256:c27c231e66336183c484fbfe080fa6cc954149366c15dc21db8b7290081ec7b8"},
-    {file = "trio-0.19.0.tar.gz", hash = "sha256:895e318e5ec5e8cea9f60b473b6edb95b215e82d99556a03eb2d20c5e027efe1"},
-]
-trio-websocket = [
-    {file = "trio-websocket-0.9.2.tar.gz", hash = "sha256:a3d34de8fac26023eee701ed1e7bf4da9a8326b61a62934ec9e53b64970fd8fe"},
-    {file = "trio_websocket-0.9.2-py3-none-any.whl", hash = "sha256:5b558f6e83cc20a37c3b61202476c5295d1addf57bd65543364e0337e37ed2bc"},
-]
-twilio = [
-    {file = "twilio-7.3.2-py2.py3-none-any.whl", hash = "sha256:6cc6ed114b07a7ce853503a5a27281f56237b411ea415012955cff3a57045f1b"},
-    {file = "twilio-7.3.2.tar.gz", hash = "sha256:3170da33c7f4293bbebcd032b183866e044fcf8418e5c5e15bdd5ec7a0a958b6"},
-]
-types-pytz = [
-    {file = "types-pytz-2021.3.1.tar.gz", hash = "sha256:dffd77f3efecd3b1555f187a9bf3a638d55fac296700b829c41bd51ec72a6eb7"},
-    {file = "types_pytz-2021.3.1-py3-none-any.whl", hash = "sha256:d58a0688094b768d8e21c044e45861cbcaecba0494fd5b9c5feb3e1739211606"},
-]
-types-pyyaml = [
-    {file = "types-PyYAML-6.0.1.tar.gz", hash = "sha256:2e27b0118ca4248a646101c5c318dc02e4ca2866d6bc42e84045dbb851555a76"},
-    {file = "types_PyYAML-6.0.1-py3-none-any.whl", hash = "sha256:d5b318269652e809b5c30a5fe666c50159ab80bfd41cd6bafe655bf20b29fcba"},
-]
-typing-extensions = [
-    {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"},
-    {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"},
-]
-urllib3 = [
-    {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"},
-    {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"},
-]
-uwsgi = [
-    {file = "uwsgi-2.0.20.tar.gz", hash = "sha256:88ab9867d8973d8ae84719cf233b7dafc54326fcaec89683c3f9f77c002cdff9"},
-]
-vine = [
-    {file = "vine-5.0.0-py2.py3-none-any.whl", hash = "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30"},
-    {file = "vine-5.0.0.tar.gz", hash = "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"},
-]
-wcwidth = [
-    {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
-    {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
-]
-webencodings = [
-    {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
-    {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
-]
-whoosh = [
-    {file = "Whoosh-2.7.4-py2.py3-none-any.whl", hash = "sha256:aa39c3c3426e3fd107dcb4bde64ca1e276a65a889d9085a6e4b54ba82420a852"},
-    {file = "Whoosh-2.7.4.tar.gz", hash = "sha256:7ca5633dbfa9e0e0fa400d3151a8a0c4bec53bd2ecedc0a67705b17565c31a83"},
-    {file = "Whoosh-2.7.4.zip", hash = "sha256:e0857375f63e9041e03fedd5b7541f97cf78917ac1b6b06c1fcc9b45375dda69"},
-]
-wrapt = [
-    {file = "wrapt-1.13.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a"},
-    {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489"},
-    {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:2dded5496e8f1592ec27079b28b6ad2a1ef0b9296d270f77b8e4a3a796cf6909"},
-    {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e94b7d9deaa4cc7bac9198a58a7240aaf87fe56c6277ee25fa5b3aa1edebd229"},
-    {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:498e6217523111d07cd67e87a791f5e9ee769f9241fcf8a379696e25806965af"},
-    {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ec7e20258ecc5174029a0f391e1b948bf2906cd64c198a9b8b281b811cbc04de"},
-    {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:87883690cae293541e08ba2da22cacaae0a092e0ed56bbba8d018cc486fbafbb"},
-    {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:f99c0489258086308aad4ae57da9e8ecf9e1f3f30fa35d5e170b4d4896554d80"},
-    {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6a03d9917aee887690aa3f1747ce634e610f6db6f6b332b35c2dd89412912bca"},
-    {file = "wrapt-1.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:936503cb0a6ed28dbfa87e8fcd0a56458822144e9d11a49ccee6d9a8adb2ac44"},
-    {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f9c51d9af9abb899bd34ace878fbec8bf357b3194a10c4e8e0a25512826ef056"},
-    {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:220a869982ea9023e163ba915077816ca439489de6d2c09089b219f4e11b6785"},
-    {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0877fe981fd76b183711d767500e6b3111378ed2043c145e21816ee589d91096"},
-    {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33"},
-    {file = "wrapt-1.13.3-cp310-cp310-win32.whl", hash = "sha256:78dea98c81915bbf510eb6a3c9c24915e4660302937b9ae05a0947164248020f"},
-    {file = "wrapt-1.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:ea3e746e29d4000cd98d572f3ee2a6050a4f784bb536f4ac1f035987fc1ed83e"},
-    {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8c73c1a2ec7c98d7eaded149f6d225a692caa1bd7b2401a14125446e9e90410d"},
-    {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:086218a72ec7d986a3eddb7707c8c4526d677c7b35e355875a0fe2918b059179"},
-    {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:e92d0d4fa68ea0c02d39f1e2f9cb5bc4b4a71e8c442207433d8db47ee79d7aa3"},
-    {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755"},
-    {file = "wrapt-1.13.3-cp35-cp35m-win32.whl", hash = "sha256:8aab36778fa9bba1a8f06a4919556f9f8c7b33102bd71b3ab307bb3fecb21851"},
-    {file = "wrapt-1.13.3-cp35-cp35m-win_amd64.whl", hash = "sha256:944b180f61f5e36c0634d3202ba8509b986b5fbaf57db3e94df11abee244ba13"},
-    {file = "wrapt-1.13.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918"},
-    {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade"},
-    {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc"},
-    {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:e6906d6f48437dfd80464f7d7af1740eadc572b9f7a4301e7dd3d65db285cacf"},
-    {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125"},
-    {file = "wrapt-1.13.3-cp36-cp36m-win32.whl", hash = "sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36"},
-    {file = "wrapt-1.13.3-cp36-cp36m-win_amd64.whl", hash = "sha256:f122ccd12fdc69628786d0c947bdd9cb2733be8f800d88b5a37c57f1f1d73c10"},
-    {file = "wrapt-1.13.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068"},
-    {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709"},
-    {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0cb23d36ed03bf46b894cfec777eec754146d68429c30431c99ef28482b5c1df"},
-    {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:96b81ae75591a795d8c90edc0bfaab44d3d41ffc1aae4d994c5aa21d9b8e19a2"},
-    {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7dd215e4e8514004c8d810a73e342c536547038fb130205ec4bba9f5de35d45b"},
-    {file = "wrapt-1.13.3-cp37-cp37m-win32.whl", hash = "sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829"},
-    {file = "wrapt-1.13.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fd76c47f20984b43d93de9a82011bb6e5f8325df6c9ed4d8310029a55fa361ea"},
-    {file = "wrapt-1.13.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b73d4b78807bd299b38e4598b8e7bd34ed55d480160d2e7fdaabd9931afa65f9"},
-    {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ec9465dd69d5657b5d2fa6133b3e1e989ae27d29471a672416fd729b429eb554"},
-    {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dd91006848eb55af2159375134d724032a2d1d13bcc6f81cd8d3ed9f2b8e846c"},
-    {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b"},
-    {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce"},
-    {file = "wrapt-1.13.3-cp38-cp38-win32.whl", hash = "sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79"},
-    {file = "wrapt-1.13.3-cp38-cp38-win_amd64.whl", hash = "sha256:7dde79d007cd6dfa65afe404766057c2409316135cb892be4b1c768e3f3a11cb"},
-    {file = "wrapt-1.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb"},
-    {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32"},
-    {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:25b1b1d5df495d82be1c9d2fad408f7ce5ca8a38085e2da41bb63c914baadff7"},
-    {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e"},
-    {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:865c0b50003616f05858b22174c40ffc27a38e67359fa1495605f96125f76640"},
-    {file = "wrapt-1.13.3-cp39-cp39-win32.whl", hash = "sha256:0a017a667d1f7411816e4bf214646d0ad5b1da2c1ea13dec6c162736ff25a374"},
-    {file = "wrapt-1.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:81bd7c90d28a4b2e1df135bfbd7c23aee3050078ca6441bead44c42483f9ebfb"},
-    {file = "wrapt-1.13.3.tar.gz", hash = "sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185"},
-]
-wsproto = [
-    {file = "wsproto-1.0.0-py3-none-any.whl", hash = "sha256:d8345d1808dd599b5ffb352c25a367adb6157e664e140dbecba3f9bc007edb9f"},
-    {file = "wsproto-1.0.0.tar.gz", hash = "sha256:868776f8456997ad0d9720f7322b746bbe9193751b5b290b7f924659377c8c38"},
-]
-yubiotp = [
-    {file = "YubiOTP-1.0.0.post1-py2.py3-none-any.whl", hash = "sha256:7ad57011866e0bc6c6d179ffbc3926fcc0e82d410178a6d01ba4da0f88332878"},
-    {file = "YubiOTP-1.0.0.post1.tar.gz", hash = "sha256:c13825f7b76a69afb92f19521f4dea9f5031d70f45123b505dc2e0ac03132065"},
-]
diff --git a/pyproject.toml b/pyproject.toml
index 68707b75ac229f3e68b91bdc9a4e34ac158b0137..f0cfec6f98e15956eabf2efa6abc60651b16504e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,11 +1,18 @@
 [tool.poetry]
 name = "AlekSIS-Core"
-version = "2.3.dev0"
+version = "2.5.1.dev0"
 packages = [
     { include = "aleksis" }
 ]
 readme = "README.rst"
-include = ["CHANGELOG.rst", "LICENCE.rst", "docs/*", "docs/**/*", "aleksis/**/*.mo", "conftest.py", "tox.ini"]
+include = [
+    { path = "aleksis/**/*.mo", format = ["sdist", "wheel"] },
+    { path = "*.rst", format = "sdist" },
+    { path = "docs/*", format = "sdist" },
+    { path = "docs/**/*", format = "sdist" },
+    { path = "conftest.py", format = "sdist" },
+    { path = "tox.ini", format = "sdist" }
+]
 
 description = "AlekSIS (School Information System) — Core"
 authors = [
@@ -57,7 +64,7 @@ django-sass-processor = "1.0"
 libsass = "^0.21.0"
 colour = "^0.1.5"
 dynaconf = {version = "^3.1", extras = ["yaml", "toml", "ini"]}
-django-auth-ldap = { version = "^3.0", optional = true }
+django-auth-ldap = { version = "^4.0", optional = true }
 django-maintenance-mode = "^0.16.0"
 django-ipware = "^4.0"
 django-impersonate = "^1.4"
@@ -74,19 +81,19 @@ html2text = "^2020.0.0"
 django-ckeditor = "^6.0.0"
 django-js-reverse = "^0.9.1"
 calendarweek = "^0.5.0"
-Celery = {version=">=5.0,<5.2", extras=["django", "redis"]}
+Celery = {version="^5.2", extras=["django", "redis"]}
 django-celery-results = "^2.0.1"
 django-celery-beat = "^2.2.0"
 django-celery-email = "^3.0.0"
 django-jsonstore = "^0.5.0"
 django-polymorphic = "^3.0.0"
-django-colorfield = "^0.5.0"
+django-colorfield = "^0.6.0"
 django-bleach = "^1.0.0"
 django-guardian = "^2.2.0"
 rules = "^3.0"
 django-cache-memoize = "^0.1.6"
 django-haystack = "^3.1"
-celery-haystack-ng = "^0.20"
+celery-haystack-ng = "^0.21.1"
 django-dbbackup = "^3.3.0"
 spdx-license-list = "^0.5.0"
 license-expression = "^21.6"
@@ -99,11 +106,13 @@ django-cachalot = "^2.3.2"
 django-prometheus = "^2.1.0"
 django-model-utils = "^4.0.0"
 bs4 = "^0.0.1"
-django-allauth = "^0.46.0"
+django-invitations = "^1.9.3"
+django-cleavejs = "^0.1.0"
+django-allauth = "^0.47.0"
 django-uwsgi-ng = "^1.1.0"
 django-extensions = "^3.1.1"
 ipython = "^7.20.0"
-django-oauth-toolkit = "^1.5.0"
+django-oauth-toolkit = "^1.6.2"
 django-redis = "^5.0.0"
 django-storages = {version = "^1.11.1", optional = true}
 boto3 = {version = "^1.17.33", optional = true}
@@ -116,11 +125,13 @@ python-gnupg = "^0.4.7"
 sentry-sdk = {version = "^1.4.3", optional = true}
 django-image-cropping = "^1.6.1"
 easy-thumbnails = "^2.8"
+django-cte = "^1.1.5"
+pycountry = "^20.7.3"
 
 [tool.poetry.extras]
 ldap = ["django-auth-ldap"]
 s3 = ["boto3", "django-storages"]
-sentry = ["sentry"]
+sentry = ["sentry-sdk"]
 
 [tool.poetry.dev-dependencies]
 aleksis-builddeps = "^5"
diff --git a/tox.ini b/tox.ini
index 6ba5d926ea520a574125f4a9717b93ba0020a2c3..4819e5b95946e3f021429e75c36fe13e7dd61d25 100644
--- a/tox.ini
+++ b/tox.ini
@@ -9,7 +9,7 @@ whitelist_externals = poetry
 skip_install = true
 envdir = {toxworkdir}/globalenv
 commands_pre =
-     poetry install
+     poetry install -E ldap
      poetry run aleksis-admin yarn install
      poetry run aleksis-admin collectstatic --no-input
 commands =